You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2023/12/13 13:32:45 UTC

(commons-exec) 01/03: Sort members

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-exec.git

commit 26be10ba84c8eeb75e0e1f0e95cdcdaf0051c301
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Wed Dec 13 08:13:24 2023 -0500

    Sort members
---
 .../java/org/apache/commons/exec/CommandLine.java  | 372 +++++-----
 .../commons/exec/DefaultExecuteResultHandler.java  |  40 +-
 .../org/apache/commons/exec/DefaultExecutor.java   | 346 +++++-----
 .../apache/commons/exec/ExecuteStreamHandler.java  |  16 +-
 .../org/apache/commons/exec/ExecuteWatchdog.java   | 166 ++---
 .../java/org/apache/commons/exec/Executor.java     | 190 +++---
 .../org/apache/commons/exec/LogOutputStream.java   |  92 +--
 src/main/java/org/apache/commons/exec/OS.java      |  72 +-
 .../org/apache/commons/exec/PumpStreamHandler.java | 186 ++---
 .../java/org/apache/commons/exec/StreamPumper.java |  28 +-
 .../java/org/apache/commons/exec/Watchdog.java     |  28 +-
 .../environment/DefaultProcessingEnvironment.java  |  84 +--
 .../commons/exec/environment/EnvironmentUtils.java |  72 +-
 .../exec/launcher/CommandLauncherFactory.java      |   6 +-
 .../exec/launcher/CommandLauncherProxy.java        |   4 +-
 .../commons/exec/launcher/VmsCommandLauncher.java  |  48 +-
 .../org/apache/commons/exec/util/MapUtils.java     |  62 +-
 .../org/apache/commons/exec/util/StringUtils.java  | 176 ++---
 .../org/apache/commons/exec/AbstractExecTest.java  |  22 +-
 .../org/apache/commons/exec/CommandLineTest.java   | 470 ++++++-------
 .../apache/commons/exec/DefaultExecutorTest.java   | 750 ++++++++++-----------
 .../apache/commons/exec/LogOutputStreamTest.java   |  72 +-
 .../java/org/apache/commons/exec/TestUtil.java     |  30 +-
 .../java/org/apache/commons/exec/TutorialTest.java |  96 +--
 .../exec/environment/EnvironmentUtilsTest.java     |  54 +-
 .../org/apache/commons/exec/issues/Exec36Test.java | 132 ++--
 .../org/apache/commons/exec/issues/Exec41Test.java |  94 +--
 .../org/apache/commons/exec/issues/Exec62Test.java |  36 +-
 .../org/apache/commons/exec/issues/Exec65Test.java |  32 +-
 .../apache/commons/exec/util/StringUtilTest.java   |  24 +-
 30 files changed, 1900 insertions(+), 1900 deletions(-)

diff --git a/src/main/java/org/apache/commons/exec/CommandLine.java b/src/main/java/org/apache/commons/exec/CommandLine.java
index e3994c09..09204cb0 100644
--- a/src/main/java/org/apache/commons/exec/CommandLine.java
+++ b/src/main/java/org/apache/commons/exec/CommandLine.java
@@ -32,24 +32,26 @@ import org.apache.commons.exec.util.StringUtils;
 public class CommandLine {
 
     /**
-     * The arguments of the command.
+     * Encapsulates a command line argument.
      */
-    private final Vector<Argument> arguments = new Vector<>();
+    final class Argument {
 
-    /**
-     * The program to execute.
-     */
-    private final String executable;
+        private final String value;
+        private final boolean handleQuoting;
 
-    /**
-     * A map of name value pairs used to expand command line arguments
-     */
-    private Map<String, ?> substitutionMap; // N.B. This can contain values other than Strings
+        private Argument(final String value, final boolean handleQuoting) {
+            this.value = value.trim();
+            this.handleQuoting = handleQuoting;
+        }
 
-    /**
-     * Was a file being used to set the executable?
-     */
-    private final boolean isFile;
+        private String getValue() {
+            return value;
+        }
+
+        private boolean isHandleQuoting() {
+            return handleQuoting;
+        }
+    }
 
     /**
      * Create a command line from a string.
@@ -90,24 +92,96 @@ public class CommandLine {
     }
 
     /**
-     * Create a command line without any arguments.
+     * Crack a command line.
      *
-     * @param executable the executable
+     * @param toProcess the command line to process
+     * @return the command line broken into strings. An empty or null toProcess parameter results in a zero sized array
      */
-    public CommandLine(final String executable) {
-        this.isFile = false;
-        this.executable = toCleanExecutable(executable);
+    private static String[] translateCommandline(final String toProcess) {
+        if (toProcess == null || toProcess.isEmpty()) {
+            // no command? no string
+            return new String[0];
+        }
+
+        // parse with a simple finite state machine
+
+        final int normal = 0;
+        final int inQuote = 1;
+        final int inDoubleQuote = 2;
+        int state = normal;
+        final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true);
+        final ArrayList<String> list = new ArrayList<>();
+        StringBuilder current = new StringBuilder();
+        boolean lastTokenHasBeenQuoted = false;
+
+        while (tok.hasMoreTokens()) {
+            final String nextTok = tok.nextToken();
+            switch (state) {
+            case inQuote:
+                if ("\'".equals(nextTok)) {
+                    lastTokenHasBeenQuoted = true;
+                    state = normal;
+                } else {
+                    current.append(nextTok);
+                }
+                break;
+            case inDoubleQuote:
+                if ("\"".equals(nextTok)) {
+                    lastTokenHasBeenQuoted = true;
+                    state = normal;
+                } else {
+                    current.append(nextTok);
+                }
+                break;
+            default:
+                if ("\'".equals(nextTok)) {
+                    state = inQuote;
+                } else if ("\"".equals(nextTok)) {
+                    state = inDoubleQuote;
+                } else if (" ".equals(nextTok)) {
+                    if (lastTokenHasBeenQuoted || current.length() != 0) {
+                        list.add(current.toString());
+                        current = new StringBuilder();
+                    }
+                } else {
+                    current.append(nextTok);
+                }
+                lastTokenHasBeenQuoted = false;
+                break;
+            }
+        }
+
+        if (lastTokenHasBeenQuoted || current.length() != 0) {
+            list.add(current.toString());
+        }
+
+        if (state == inQuote || state == inDoubleQuote) {
+            throw new IllegalArgumentException("Unbalanced quotes in " + toProcess);
+        }
+
+        final String[] args = new String[list.size()];
+        return list.toArray(args);
     }
 
     /**
-     * Create a command line without any arguments.
-     *
-     * @param executable the executable file
+     * The arguments of the command.
      */
-    public CommandLine(final File executable) {
-        this.isFile = true;
-        this.executable = toCleanExecutable(executable.getAbsolutePath());
-    }
+    private final Vector<Argument> arguments = new Vector<>();
+
+    /**
+     * The program to execute.
+     */
+    private final String executable;
+
+    /**
+     * A map of name value pairs used to expand command line arguments
+     */
+    private Map<String, ?> substitutionMap; // N.B. This can contain values other than Strings
+
+    /**
+     * Was a file being used to set the executable?
+     */
+    private final boolean isFile;
 
     /**
      * Copy constructor.
@@ -125,50 +199,56 @@ public class CommandLine {
     }
 
     /**
-     * Returns the executable.
+     * Create a command line without any arguments.
      *
-     * @return The executable
+     * @param executable the executable file
      */
-    public String getExecutable() {
-        // Expand the executable and replace '/' and '\\' with the platform
-        // specific file separator char. This is safe here since we know
-        // that this is a platform specific command.
-        return StringUtils.fixFileSeparatorChar(expandArgument(executable));
+    public CommandLine(final File executable) {
+        this.isFile = true;
+        this.executable = toCleanExecutable(executable.getAbsolutePath());
     }
 
     /**
-     * Was a file being used to set the executable?
+     * Create a command line without any arguments.
      *
-     * @return true if a file was used for setting the executable
+     * @param executable the executable
      */
-    public boolean isFile() {
-        return isFile;
+    public CommandLine(final String executable) {
+        this.isFile = false;
+        this.executable = toCleanExecutable(executable);
     }
 
     /**
-     * Add multiple arguments. Handles parsing of quotes and whitespace.
+     * Add a single argument. Handles quoting.
      *
-     * @param addArguments An array of arguments
+     * @param argument The argument to add
      * @return The command line itself
+     * @throws IllegalArgumentException If argument contains both single and double quotes
      */
-    public CommandLine addArguments(final String[] addArguments) {
-        return this.addArguments(addArguments, true);
+    public CommandLine addArgument(final String argument) {
+        return this.addArgument(argument, true);
     }
 
     /**
-     * Add multiple arguments.
+     * Add a single argument.
      *
-     * @param addArguments An array of arguments
+     * @param argument The argument to add
      * @param handleQuoting Add the argument with/without handling quoting
      * @return The command line itself
      */
-    public CommandLine addArguments(final String[] addArguments, final boolean handleQuoting) {
-        if (addArguments != null) {
-            for (final String addArgument : addArguments) {
-                addArgument(addArgument, handleQuoting);
-            }
+    public CommandLine addArgument(final String argument, final boolean handleQuoting) {
+
+        if (argument == null) {
+            return this;
+        }
+
+        // check if we can really quote the argument - if not throw an
+        // IllegalArgumentException
+        if (handleQuoting) {
+            StringUtils.quoteArgument(argument);
         }
 
+        arguments.add(new Argument(argument, handleQuoting));
         return this;
     }
 
@@ -201,39 +281,43 @@ public class CommandLine {
     }
 
     /**
-     * Add a single argument. Handles quoting.
+     * Add multiple arguments. Handles parsing of quotes and whitespace.
      *
-     * @param argument The argument to add
+     * @param addArguments An array of arguments
      * @return The command line itself
-     * @throws IllegalArgumentException If argument contains both single and double quotes
      */
-    public CommandLine addArgument(final String argument) {
-        return this.addArgument(argument, true);
+    public CommandLine addArguments(final String[] addArguments) {
+        return this.addArguments(addArguments, true);
     }
 
     /**
-     * Add a single argument.
+     * Add multiple arguments.
      *
-     * @param argument The argument to add
+     * @param addArguments An array of arguments
      * @param handleQuoting Add the argument with/without handling quoting
      * @return The command line itself
      */
-    public CommandLine addArgument(final String argument, final boolean handleQuoting) {
-
-        if (argument == null) {
-            return this;
-        }
-
-        // check if we can really quote the argument - if not throw an
-        // IllegalArgumentException
-        if (handleQuoting) {
-            StringUtils.quoteArgument(argument);
+    public CommandLine addArguments(final String[] addArguments, final boolean handleQuoting) {
+        if (addArguments != null) {
+            for (final String addArgument : addArguments) {
+                addArgument(addArgument, handleQuoting);
+            }
         }
 
-        arguments.add(new Argument(argument, handleQuoting));
         return this;
     }
 
+    /**
+     * Expand variables in a command line argument.
+     *
+     * @param argument the argument
+     * @return the expanded string
+     */
+    private String expandArgument(final String argument) {
+        final StringBuffer stringBuffer = StringUtils.stringSubstitution(argument, this.getSubstitutionMap(), true);
+        return stringBuffer.toString();
+    }
+
     /**
      * Returns the expanded and quoted command line arguments.
      *
@@ -255,127 +339,42 @@ public class CommandLine {
     }
 
     /**
-     * @return the substitution map
-     */
-    public Map<String, ?> getSubstitutionMap() {
-        return substitutionMap;
-    }
-
-    /**
-     * Sets the substitutionMap to expand variables in the command line.
+     * Returns the executable.
      *
-     * @param substitutionMap the map
+     * @return The executable
      */
-    public void setSubstitutionMap(final Map<String, ?> substitutionMap) {
-        this.substitutionMap = substitutionMap;
+    public String getExecutable() {
+        // Expand the executable and replace '/' and '\\' with the platform
+        // specific file separator char. This is safe here since we know
+        // that this is a platform specific command.
+        return StringUtils.fixFileSeparatorChar(expandArgument(executable));
     }
 
     /**
-     * Returns the command line as an array of strings.
-     *
-     * @return The command line as an string array
+     * @return the substitution map
      */
-    public String[] toStrings() {
-        final String[] result = new String[arguments.size() + 1];
-        result[0] = this.getExecutable();
-        System.arraycopy(getArguments(), 0, result, 1, result.length - 1);
-        return result;
+    public Map<String, ?> getSubstitutionMap() {
+        return substitutionMap;
     }
 
     /**
-     * Stringify operator returns the command line as a string. Parameters are correctly quoted when containing a space or left untouched if the are already
-     * quoted.
+     * Was a file being used to set the executable?
      *
-     * @return the command line as single string
+     * @return true if a file was used for setting the executable
      */
-    @Override
-    public String toString() {
-        return "[" + StringUtils.toString(toStrings(), ", ") + "]";
+    public boolean isFile() {
+        return isFile;
     }
 
     // --- Implementation ---------------------------------------------------
 
     /**
-     * Expand variables in a command line argument.
-     *
-     * @param argument the argument
-     * @return the expanded string
-     */
-    private String expandArgument(final String argument) {
-        final StringBuffer stringBuffer = StringUtils.stringSubstitution(argument, this.getSubstitutionMap(), true);
-        return stringBuffer.toString();
-    }
-
-    /**
-     * Crack a command line.
+     * Sets the substitutionMap to expand variables in the command line.
      *
-     * @param toProcess the command line to process
-     * @return the command line broken into strings. An empty or null toProcess parameter results in a zero sized array
+     * @param substitutionMap the map
      */
-    private static String[] translateCommandline(final String toProcess) {
-        if (toProcess == null || toProcess.isEmpty()) {
-            // no command? no string
-            return new String[0];
-        }
-
-        // parse with a simple finite state machine
-
-        final int normal = 0;
-        final int inQuote = 1;
-        final int inDoubleQuote = 2;
-        int state = normal;
-        final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true);
-        final ArrayList<String> list = new ArrayList<>();
-        StringBuilder current = new StringBuilder();
-        boolean lastTokenHasBeenQuoted = false;
-
-        while (tok.hasMoreTokens()) {
-            final String nextTok = tok.nextToken();
-            switch (state) {
-            case inQuote:
-                if ("\'".equals(nextTok)) {
-                    lastTokenHasBeenQuoted = true;
-                    state = normal;
-                } else {
-                    current.append(nextTok);
-                }
-                break;
-            case inDoubleQuote:
-                if ("\"".equals(nextTok)) {
-                    lastTokenHasBeenQuoted = true;
-                    state = normal;
-                } else {
-                    current.append(nextTok);
-                }
-                break;
-            default:
-                if ("\'".equals(nextTok)) {
-                    state = inQuote;
-                } else if ("\"".equals(nextTok)) {
-                    state = inDoubleQuote;
-                } else if (" ".equals(nextTok)) {
-                    if (lastTokenHasBeenQuoted || current.length() != 0) {
-                        list.add(current.toString());
-                        current = new StringBuilder();
-                    }
-                } else {
-                    current.append(nextTok);
-                }
-                lastTokenHasBeenQuoted = false;
-                break;
-            }
-        }
-
-        if (lastTokenHasBeenQuoted || current.length() != 0) {
-            list.add(current.toString());
-        }
-
-        if (state == inQuote || state == inDoubleQuote) {
-            throw new IllegalArgumentException("Unbalanced quotes in " + toProcess);
-        }
-
-        final String[] args = new String[list.size()];
-        return list.toArray(args);
+    public void setSubstitutionMap(final Map<String, ?> substitutionMap) {
+        this.substitutionMap = substitutionMap;
     }
 
     /**
@@ -395,24 +394,25 @@ public class CommandLine {
     }
 
     /**
-     * Encapsulates a command line argument.
+     * Stringify operator returns the command line as a string. Parameters are correctly quoted when containing a space or left untouched if the are already
+     * quoted.
+     *
+     * @return the command line as single string
      */
-    final class Argument {
-
-        private final String value;
-        private final boolean handleQuoting;
-
-        private Argument(final String value, final boolean handleQuoting) {
-            this.value = value.trim();
-            this.handleQuoting = handleQuoting;
-        }
-
-        private String getValue() {
-            return value;
-        }
+    @Override
+    public String toString() {
+        return "[" + StringUtils.toString(toStrings(), ", ") + "]";
+    }
 
-        private boolean isHandleQuoting() {
-            return handleQuoting;
-        }
+    /**
+     * Returns the command line as an array of strings.
+     *
+     * @return The command line as an string array
+     */
+    public String[] toStrings() {
+        final String[] result = new String[arguments.size() + 1];
+        result[0] = this.getExecutable();
+        System.arraycopy(getArguments(), 0, result, 1, result.length - 1);
+        return result;
     }
 }
diff --git a/src/main/java/org/apache/commons/exec/DefaultExecuteResultHandler.java b/src/main/java/org/apache/commons/exec/DefaultExecuteResultHandler.java
index 08294efc..9414c221 100644
--- a/src/main/java/org/apache/commons/exec/DefaultExecuteResultHandler.java
+++ b/src/main/java/org/apache/commons/exec/DefaultExecuteResultHandler.java
@@ -43,26 +43,6 @@ public class DefaultExecuteResultHandler implements ExecuteResultHandler {
         this.exitValue = Executor.INVALID_EXITVALUE;
     }
 
-    /**
-     * @see org.apache.commons.exec.ExecuteResultHandler#onProcessComplete(int)
-     */
-    @Override
-    public void onProcessComplete(final int exitValue) {
-        this.exitValue = exitValue;
-        this.exception = null;
-        this.hasResult = true;
-    }
-
-    /**
-     * @see org.apache.commons.exec.ExecuteResultHandler#onProcessFailed(org.apache.commons.exec.ExecuteException)
-     */
-    @Override
-    public void onProcessFailed(final ExecuteException e) {
-        this.exitValue = e.getExitValue();
-        this.exception = e;
-        this.hasResult = true;
-    }
-
     /**
      * Gets the {@code exception} causing the process execution to fail.
      *
@@ -102,6 +82,26 @@ public class DefaultExecuteResultHandler implements ExecuteResultHandler {
         return hasResult;
     }
 
+    /**
+     * @see org.apache.commons.exec.ExecuteResultHandler#onProcessComplete(int)
+     */
+    @Override
+    public void onProcessComplete(final int exitValue) {
+        this.exitValue = exitValue;
+        this.exception = null;
+        this.hasResult = true;
+    }
+
+    /**
+     * @see org.apache.commons.exec.ExecuteResultHandler#onProcessFailed(org.apache.commons.exec.ExecuteException)
+     */
+    @Override
+    public void onProcessFailed(final ExecuteException e) {
+        this.exitValue = e.getExitValue();
+        this.exception = e;
+        this.hasResult = true;
+    }
+
     /**
      * Causes the current thread to wait, if necessary, until the
      * process has terminated. This method returns immediately if
diff --git a/src/main/java/org/apache/commons/exec/DefaultExecutor.java b/src/main/java/org/apache/commons/exec/DefaultExecutor.java
index 6b257ca1..851d7307 100644
--- a/src/main/java/org/apache/commons/exec/DefaultExecutor.java
+++ b/src/main/java/org/apache/commons/exec/DefaultExecutor.java
@@ -88,76 +88,63 @@ public class DefaultExecutor implements Executor {
     }
 
     /**
-     * @see org.apache.commons.exec.Executor#getStreamHandler()
-     */
-    @Override
-    public ExecuteStreamHandler getStreamHandler() {
-        return streamHandler;
-    }
-
-    /**
-     * @see org.apache.commons.exec.Executor#setStreamHandler(org.apache.commons.exec.ExecuteStreamHandler)
-     */
-    @Override
-    public void setStreamHandler(final ExecuteStreamHandler streamHandler) {
-        this.streamHandler = streamHandler;
-    }
-
-    /**
-     * @see org.apache.commons.exec.Executor#getWatchdog()
+     * Close the streams belonging to the given Process.
+     *
+     * @param process the <CODE>Process</CODE>.
      */
-    @Override
-    public ExecuteWatchdog getWatchdog() {
-        return watchdog;
-    }
+    private void closeProcessStreams(final Process process) {
 
-    /**
-     * @see org.apache.commons.exec.Executor#setWatchdog(org.apache.commons.exec.ExecuteWatchdog)
-     */
-    @Override
-    public void setWatchdog(final ExecuteWatchdog watchDog) {
-        this.watchdog = watchDog;
-    }
+        try {
+            process.getInputStream().close();
+        }
+        catch (final IOException e) {
+            setExceptionCaught(e);
+        }
 
-    /**
-     * @see org.apache.commons.exec.Executor#getProcessDestroyer()
-     */
-    @Override
-    public ProcessDestroyer getProcessDestroyer() {
-      return this.processDestroyer;
-    }
+        try {
+            process.getOutputStream().close();
+        }
+        catch (final IOException e) {
+            setExceptionCaught(e);
+        }
 
-    /**
-     * @see org.apache.commons.exec.Executor#setProcessDestroyer(ProcessDestroyer)
-     */
-    @Override
-    public void setProcessDestroyer(final ProcessDestroyer processDestroyer) {
-      this.processDestroyer = processDestroyer;
+        try {
+            process.getErrorStream().close();
+        }
+        catch (final IOException e) {
+            setExceptionCaught(e);
+        }
     }
 
     /**
-     * @see org.apache.commons.exec.Executor#getWorkingDirectory()
+     * Factory method to create a thread waiting for the result of an
+     * asynchronous execution.
+     *
+     * @param runnable the runnable passed to the thread
+     * @param name the name of the thread
+     * @return the thread
      */
-    @Override
-    public File getWorkingDirectory() {
-        return workingDirectory;
+    protected Thread createThread(final Runnable runnable, final String name) {
+        return new Thread(runnable, name);
     }
 
     /**
-     * @see org.apache.commons.exec.Executor#setWorkingDirectory(java.io.File)
+     * @see org.apache.commons.exec.Executor#execute(CommandLine)
      */
     @Override
-    public void setWorkingDirectory(final File dir) {
-        this.workingDirectory = dir;
+    public int execute(final CommandLine command) throws ExecuteException,
+            IOException {
+        return execute(command, (Map<String, String>) null);
     }
 
     /**
-     * @see org.apache.commons.exec.Executor#execute(CommandLine)
+     * @see org.apache.commons.exec.Executor#execute(CommandLine,
+     *      org.apache.commons.exec.ExecuteResultHandler)
      */
     @Override
-    public int execute(final CommandLine command) throws ExecuteException,
-            IOException {
-        return execute(command, (Map<String, String>) null);
+    public void execute(final CommandLine command, final ExecuteResultHandler handler)
+            throws ExecuteException, IOException {
+        execute(command, null, handler);
     }
 
     /**
@@ -175,16 +162,6 @@ public class DefaultExecutor implements Executor {
 
     }
 
-    /**
-     * @see org.apache.commons.exec.Executor#execute(CommandLine,
-     *      org.apache.commons.exec.ExecuteResultHandler)
-     */
-    @Override
-    public void execute(final CommandLine command, final ExecuteResultHandler handler)
-            throws ExecuteException, IOException {
-        execute(command, null, handler);
-    }
-
     /**
      * @see org.apache.commons.exec.Executor#execute(CommandLine,
      *      java.util.Map, org.apache.commons.exec.ExecuteResultHandler)
@@ -215,112 +192,6 @@ public class DefaultExecutor implements Executor {
         getExecutorThread().start();
     }
 
-    /** @see org.apache.commons.exec.Executor#setExitValue(int) */
-    @Override
-    public void setExitValue(final int value) {
-        this.setExitValues(new int[] {value});
-    }
-
-    /** @see org.apache.commons.exec.Executor#setExitValues(int[]) */
-    @Override
-    public void setExitValues(final int[] values) {
-        this.exitValues = values == null ? null : (int[]) values.clone();
-    }
-
-    /** @see org.apache.commons.exec.Executor#isFailure(int) */
-    @Override
-    public boolean isFailure(final int exitValue) {
-
-        if (this.exitValues == null) {
-            return false;
-        }
-        if (this.exitValues.length == 0) {
-            return this.launcher.isFailure(exitValue);
-        }
-        for (final int exitValue2 : this.exitValues) {
-            if (exitValue2 == exitValue) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Factory method to create a thread waiting for the result of an
-     * asynchronous execution.
-     *
-     * @param runnable the runnable passed to the thread
-     * @param name the name of the thread
-     * @return the thread
-     */
-    protected Thread createThread(final Runnable runnable, final String name) {
-        return new Thread(runnable, name);
-    }
-
-    /**
-     * Creates a process that runs a command.
-     *
-     * @param command
-     *            the command to run
-     * @param env
-     *            the environment for the command
-     * @param dir
-     *            the working directory for the command
-     * @return the process started
-     * @throws IOException
-     *             forwarded from the particular launcher used
-     */
-    protected Process launch(final CommandLine command, final Map<String, String> env,
-            final File dir) throws IOException {
-
-        if (this.launcher == null) {
-            throw new IllegalStateException("CommandLauncher can not be null");
-        }
-
-        if (dir != null && !dir.exists()) {
-            throw new IOException(dir + " doesn't exist.");
-        }
-        return this.launcher.exec(command, env, dir);
-    }
-
-    /**
-     * Gets the worker thread being used for asynchronous execution.
-     *
-     * @return the worker thread
-     */
-    protected Thread getExecutorThread() {
-        return executorThread;
-    }
-
-    /**
-     * Close the streams belonging to the given Process.
-     *
-     * @param process the <CODE>Process</CODE>.
-     */
-    private void closeProcessStreams(final Process process) {
-
-        try {
-            process.getInputStream().close();
-        }
-        catch (final IOException e) {
-            setExceptionCaught(e);
-        }
-
-        try {
-            process.getOutputStream().close();
-        }
-        catch (final IOException e) {
-            setExceptionCaught(e);
-        }
-
-        try {
-            process.getErrorStream().close();
-        }
-        catch (final IOException e) {
-            setExceptionCaught(e);
-        }
-    }
-
     /**
      * Execute an internal process. If the executing thread is interrupted while waiting for the
      * child process to return the child process will be killed.
@@ -432,6 +303,100 @@ public class DefaultExecutor implements Executor {
         }
     }
 
+    /**
+     * Gets the first IOException being thrown.
+     *
+     * @return the first IOException being caught
+     */
+    private IOException getExceptionCaught() {
+        return this.exceptionCaught;
+    }
+
+    /**
+     * Gets the worker thread being used for asynchronous execution.
+     *
+     * @return the worker thread
+     */
+    protected Thread getExecutorThread() {
+        return executorThread;
+    }
+
+    /**
+     * @see org.apache.commons.exec.Executor#getProcessDestroyer()
+     */
+    @Override
+    public ProcessDestroyer getProcessDestroyer() {
+      return this.processDestroyer;
+    }
+
+    /**
+     * @see org.apache.commons.exec.Executor#getStreamHandler()
+     */
+    @Override
+    public ExecuteStreamHandler getStreamHandler() {
+        return streamHandler;
+    }
+
+    /**
+     * @see org.apache.commons.exec.Executor#getWatchdog()
+     */
+    @Override
+    public ExecuteWatchdog getWatchdog() {
+        return watchdog;
+    }
+
+    /**
+     * @see org.apache.commons.exec.Executor#getWorkingDirectory()
+     */
+    @Override
+    public File getWorkingDirectory() {
+        return workingDirectory;
+    }
+
+    /** @see org.apache.commons.exec.Executor#isFailure(int) */
+    @Override
+    public boolean isFailure(final int exitValue) {
+
+        if (this.exitValues == null) {
+            return false;
+        }
+        if (this.exitValues.length == 0) {
+            return this.launcher.isFailure(exitValue);
+        }
+        for (final int exitValue2 : this.exitValues) {
+            if (exitValue2 == exitValue) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Creates a process that runs a command.
+     *
+     * @param command
+     *            the command to run
+     * @param env
+     *            the environment for the command
+     * @param dir
+     *            the working directory for the command
+     * @return the process started
+     * @throws IOException
+     *             forwarded from the particular launcher used
+     */
+    protected Process launch(final CommandLine command, final Map<String, String> env,
+            final File dir) throws IOException {
+
+        if (this.launcher == null) {
+            throw new IllegalStateException("CommandLauncher can not be null");
+        }
+
+        if (dir != null && !dir.exists()) {
+            throw new IOException(dir + " doesn't exist.");
+        }
+        return this.launcher.exec(command, env, dir);
+    }
+
     /**
      * Keep track of the first IOException being thrown.
      *
@@ -443,13 +408,48 @@ public class DefaultExecutor implements Executor {
         }
     }
 
+    /** @see org.apache.commons.exec.Executor#setExitValue(int) */
+    @Override
+    public void setExitValue(final int value) {
+        this.setExitValues(new int[] {value});
+    }
+
+    /** @see org.apache.commons.exec.Executor#setExitValues(int[]) */
+    @Override
+    public void setExitValues(final int[] values) {
+        this.exitValues = values == null ? null : (int[]) values.clone();
+    }
+
     /**
-     * Gets the first IOException being thrown.
-     *
-     * @return the first IOException being caught
+     * @see org.apache.commons.exec.Executor#setProcessDestroyer(ProcessDestroyer)
      */
-    private IOException getExceptionCaught() {
-        return this.exceptionCaught;
+    @Override
+    public void setProcessDestroyer(final ProcessDestroyer processDestroyer) {
+      this.processDestroyer = processDestroyer;
+    }
+
+    /**
+     * @see org.apache.commons.exec.Executor#setStreamHandler(org.apache.commons.exec.ExecuteStreamHandler)
+     */
+    @Override
+    public void setStreamHandler(final ExecuteStreamHandler streamHandler) {
+        this.streamHandler = streamHandler;
+    }
+
+    /**
+     * @see org.apache.commons.exec.Executor#setWatchdog(org.apache.commons.exec.ExecuteWatchdog)
+     */
+    @Override
+    public void setWatchdog(final ExecuteWatchdog watchDog) {
+        this.watchdog = watchDog;
+    }
+
+    /**
+     * @see org.apache.commons.exec.Executor#setWorkingDirectory(java.io.File)
+     */
+    @Override
+    public void setWorkingDirectory(final File dir) {
+        this.workingDirectory = dir;
     }
 
 }
diff --git a/src/main/java/org/apache/commons/exec/ExecuteStreamHandler.java b/src/main/java/org/apache/commons/exec/ExecuteStreamHandler.java
index b82a2044..95f45958 100644
--- a/src/main/java/org/apache/commons/exec/ExecuteStreamHandler.java
+++ b/src/main/java/org/apache/commons/exec/ExecuteStreamHandler.java
@@ -28,24 +28,24 @@ import java.io.OutputStream;
 public interface ExecuteStreamHandler {
 
     /**
-     * Install a handler for the input stream of the subprocess.
+     * Install a handler for the error stream of the subprocess.
      *
-     * @param os
-     *            output stream to write to the standard input stream of the subprocess
+     * @param is
+     *            input stream to read from the error stream from the subprocess
      * @throws IOException
      *             thrown when an I/O exception occurs.
      */
-    void setProcessInputStream(OutputStream os) throws IOException;
+    void setProcessErrorStream(InputStream is) throws IOException;
 
     /**
-     * Install a handler for the error stream of the subprocess.
+     * Install a handler for the input stream of the subprocess.
      *
-     * @param is
-     *            input stream to read from the error stream from the subprocess
+     * @param os
+     *            output stream to write to the standard input stream of the subprocess
      * @throws IOException
      *             thrown when an I/O exception occurs.
      */
-    void setProcessErrorStream(InputStream is) throws IOException;
+    void setProcessInputStream(OutputStream os) throws IOException;
 
     /**
      * Install a handler for the output stream of the subprocess.
diff --git a/src/main/java/org/apache/commons/exec/ExecuteWatchdog.java b/src/main/java/org/apache/commons/exec/ExecuteWatchdog.java
index 43dacb17..6a15cd08 100644
--- a/src/main/java/org/apache/commons/exec/ExecuteWatchdog.java
+++ b/src/main/java/org/apache/commons/exec/ExecuteWatchdog.java
@@ -95,50 +95,25 @@ public class ExecuteWatchdog implements TimeoutObserver {
     }
 
     /**
-     * Watches the given process and terminates it, if it runs for too long. All
-     * information from the previous run are reset.
+     * This method will rethrow the exception that was possibly caught during
+     * the run of the process. It will only remains valid once the process has
+     * been terminated either by 'error', timeout or manual intervention.
+     * Information will be discarded once a new process is ran.
      *
-     * @param processToMonitor
-     *            the process to monitor. It cannot be {@code null}
-     * @throws IllegalStateException
-     *             if a process is still being monitored.
+     * @throws Exception
+     *             a wrapped exception over the one that was silently swallowed
+     *             and stored during the process run.
      */
-    public synchronized void start(final Process processToMonitor) {
-        Objects.requireNonNull(processToMonitor, "processToMonitor");
-        if (this.process != null) {
-            throw new IllegalStateException("Already running.");
-        }
-        this.caught = null;
-        this.killedProcess = false;
-        this.watch = true;
-        this.process = processToMonitor;
-        this.processStarted = true;
-        this.notifyAll();
-        if (this.hasWatchdog) {
-            watchdog.start();
+    public synchronized void checkException() throws Exception {
+        if (caught != null) {
+            throw caught;
         }
     }
 
     /**
-     * Notification that starting the process failed.
-     *
-     * @param e the offending exception
-     *
-     */
-    public synchronized void failedToStart(final Exception e) {
-        this.processStarted = true;
-        this.caught = e;
-        this.notifyAll();
-    }
-
-    /**
-     * Stops the watcher. It will notify all threads possibly waiting on this
-     * object.
+     * reset the monitor flag and the process.
      */
-    public synchronized void stop() {
-        if (hasWatchdog) {
-            watchdog.stop();
-        }
+    protected synchronized void cleanUp() {
         watch = false;
         process = null;
     }
@@ -153,47 +128,30 @@ public class ExecuteWatchdog implements TimeoutObserver {
     }
 
     /**
-     * Called after watchdog has finished.
+     * Ensures that the process is started or not already terminated
+     * so we do not race with asynch executionor hang forever. The
+     * caller of this method must be holding the lock on this
      */
-    @Override
-    public synchronized void timeoutOccured(final Watchdog w) {
-        try {
+    private void ensureStarted() {
+        while (!processStarted && caught == null) {
             try {
-                // We must check if the process was not stopped
-                // before being here
-                if (process != null) {
-                    process.exitValue();
-                }
-            } catch (final IllegalThreadStateException itse) {
-                // the process is not terminated, if this is really
-                // a timeout and not a manual stop then destroy it.
-                if (watch) {
-                    killedProcess = true;
-                    process.destroy();
-                }
+                this.wait();
+            } catch (final InterruptedException e) {
+                throw new IllegalStateException(e.getMessage(), e);
             }
-        } catch (final Exception e) {
-            caught = e;
-            DebugUtils.handleException("Getting the exit value of the process failed", e);
-        } finally {
-            cleanUp();
         }
     }
 
     /**
-     * This method will rethrow the exception that was possibly caught during
-     * the run of the process. It will only remains valid once the process has
-     * been terminated either by 'error', timeout or manual intervention.
-     * Information will be discarded once a new process is ran.
+     * Notification that starting the process failed.
+     *
+     * @param e the offending exception
      *
-     * @throws Exception
-     *             a wrapped exception over the one that was silently swallowed
-     *             and stored during the process run.
      */
-    public synchronized void checkException() throws Exception {
-        if (caught != null) {
-            throw caught;
-        }
+    public synchronized void failedToStart(final Exception e) {
+        this.processStarted = true;
+        this.caught = e;
+        this.notifyAll();
     }
 
     /**
@@ -217,30 +175,72 @@ public class ExecuteWatchdog implements TimeoutObserver {
         return killedProcess;
     }
 
+    void setProcessNotStarted() {
+        processStarted = false;
+    }
+
     /**
-     * reset the monitor flag and the process.
+     * Watches the given process and terminates it, if it runs for too long. All
+     * information from the previous run are reset.
+     *
+     * @param processToMonitor
+     *            the process to monitor. It cannot be {@code null}
+     * @throws IllegalStateException
+     *             if a process is still being monitored.
      */
-    protected synchronized void cleanUp() {
-        watch = false;
-        process = null;
+    public synchronized void start(final Process processToMonitor) {
+        Objects.requireNonNull(processToMonitor, "processToMonitor");
+        if (this.process != null) {
+            throw new IllegalStateException("Already running.");
+        }
+        this.caught = null;
+        this.killedProcess = false;
+        this.watch = true;
+        this.process = processToMonitor;
+        this.processStarted = true;
+        this.notifyAll();
+        if (this.hasWatchdog) {
+            watchdog.start();
+        }
     }
 
-    void setProcessNotStarted() {
-        processStarted = false;
+    /**
+     * Stops the watcher. It will notify all threads possibly waiting on this
+     * object.
+     */
+    public synchronized void stop() {
+        if (hasWatchdog) {
+            watchdog.stop();
+        }
+        watch = false;
+        process = null;
     }
 
     /**
-     * Ensures that the process is started or not already terminated
-     * so we do not race with asynch executionor hang forever. The
-     * caller of this method must be holding the lock on this
+     * Called after watchdog has finished.
      */
-    private void ensureStarted() {
-        while (!processStarted && caught == null) {
+    @Override
+    public synchronized void timeoutOccured(final Watchdog w) {
+        try {
             try {
-                this.wait();
-            } catch (final InterruptedException e) {
-                throw new IllegalStateException(e.getMessage(), e);
+                // We must check if the process was not stopped
+                // before being here
+                if (process != null) {
+                    process.exitValue();
+                }
+            } catch (final IllegalThreadStateException itse) {
+                // the process is not terminated, if this is really
+                // a timeout and not a manual stop then destroy it.
+                if (watch) {
+                    killedProcess = true;
+                    process.destroy();
+                }
             }
+        } catch (final Exception e) {
+            caught = e;
+            DebugUtils.handleException("Getting the exit value of the process failed", e);
+        } finally {
+            cleanUp();
         }
     }
 }
diff --git a/src/main/java/org/apache/commons/exec/Executor.java b/src/main/java/org/apache/commons/exec/Executor.java
index 359bd5df..23484037 100644
--- a/src/main/java/org/apache/commons/exec/Executor.java
+++ b/src/main/java/org/apache/commons/exec/Executor.java
@@ -49,61 +49,73 @@ public interface Executor {
     int INVALID_EXITVALUE = 0xdeadbeef;
 
     /**
-     * Define the {@code exitValue} of the process to be considered
-     * successful. If a different exit value is returned by
-     * the process then {@link org.apache.commons.exec.Executor#execute(CommandLine)}
-     * will throw an {@link org.apache.commons.exec.ExecuteException}
+     * Methods for starting synchronous execution. The child process inherits
+     * all environment variables of the parent process.
      *
-     * @param value the exit code representing successful execution
+     * @param command the command to execute
+     * @return process exit value
+     * @throws ExecuteException execution of subprocess failed or the
+     *          subprocess returned a exit value indicating a failure
+     *          {@link Executor#setExitValue(int)}.
      */
-    void setExitValue(final int value);
+    int execute(CommandLine command)
+        throws ExecuteException, IOException;
 
     /**
-     * Define a list of {@code exitValue} of the process to be considered
-     * successful. The caller can pass one of the following values
-     * <ul>
-     *  <li>an array of exit values to be considered successful</li>
-     *  <li>an empty array for auto-detect of successful exit codes relying on {@link org.apache.commons.exec.Executor#isFailure(int)}</li>
-     *  <li>null to indicate to skip checking of exit codes</li>
-     * </ul>
+     * Methods for starting asynchronous execution. The child process inherits
+     * all environment variables of the parent process. Result provided to
+     * callback handler.
      *
-     * If an undefined exit value is returned by the process then
-     * {@link org.apache.commons.exec.Executor#execute(CommandLine)}  will
-     * throw an {@link org.apache.commons.exec.ExecuteException}.
+     * @param command the command to execute
+     * @param handler capture process termination and exit code
+     * @throws ExecuteException execution of subprocess failed
+     */
+    void execute(CommandLine command, ExecuteResultHandler handler)
+        throws ExecuteException, IOException;
+
+    /**
+     * Methods for starting synchronous execution.
      *
-     * @param values a list of the exit codes
+     * @param command the command to execute
+     * @param environment The environment for the new process. If null, the
+     *          environment of the current process is used.
+     * @return process exit value
+     * @throws ExecuteException execution of subprocess failed or the
+     *          subprocess returned a exit value indicating a failure
+     *          {@link Executor#setExitValue(int)}.
      */
-    void setExitValues(final int[] values);
+    int execute(CommandLine command, Map<String, String> environment)
+        throws ExecuteException, IOException;
 
     /**
-     * Checks whether {@code exitValue} signals a failure. If no
-     * exit values are set than the default conventions of the OS is
-     * used. e.g. most OS regard an exit code of '0' as successful
-     * execution and everything else as failure.
+     * Methods for starting asynchronous execution. The child process inherits
+     * all environment variables of the parent process. Result provided to
+     * callback handler.
      *
-     * @param exitValue the exit value (return code) to be checked
-     * @return {@code true} if {@code exitValue} signals a failure
+     * @param command the command to execute
+     * @param environment The environment for the new process. If null, the
+     *          environment of the current process is used.
+     * @param handler capture process termination and exit code
+     * @throws ExecuteException execution of subprocess failed
      */
-    boolean isFailure(final int exitValue);
+    void execute(CommandLine command, Map<String, String> environment, ExecuteResultHandler handler)
+        throws ExecuteException, IOException;
 
     /**
-     * Gets the StreamHandler used for providing input and
-     * retrieving the output.
+     * Sets the handler for cleanup of started processes if the main process
+     * is going to terminate.
      *
-     * @return the StreamHandler
+     * @return the ProcessDestroyer
      */
-    ExecuteStreamHandler getStreamHandler();
+    ProcessDestroyer getProcessDestroyer();
 
     /**
-     * Sets a custom the StreamHandler used for providing
-     * input and retrieving the output. If you don't provide
-     * a proper stream handler the executed process might block
-     * when writing to stdout and/or stderr (see
-     * {@link Process Process}).
+     * Gets the StreamHandler used for providing input and
+     * retrieving the output.
      *
-     * @param streamHandler the stream handler
+     * @return the StreamHandler
      */
-    void setStreamHandler(ExecuteStreamHandler streamHandler);
+    ExecuteStreamHandler getStreamHandler();
 
     /**
      * Gets the watchdog used to kill of processes running,
@@ -114,94 +126,82 @@ public interface Executor {
     ExecuteWatchdog getWatchdog();
 
     /**
-     * Sets the watchdog used to kill of processes running,
-     * typically, too long time.
+     * Gets the working directory of the created process.
      *
-     * @param watchDog the watchdog
+     * @return the working directory
      */
-    void setWatchdog(ExecuteWatchdog watchDog);
+    File getWorkingDirectory();
 
     /**
-     * Sets the handler for cleanup of started processes if the main process
-     * is going to terminate.
+     * Checks whether {@code exitValue} signals a failure. If no
+     * exit values are set than the default conventions of the OS is
+     * used. e.g. most OS regard an exit code of '0' as successful
+     * execution and everything else as failure.
      *
-     * @return the ProcessDestroyer
+     * @param exitValue the exit value (return code) to be checked
+     * @return {@code true} if {@code exitValue} signals a failure
      */
-    ProcessDestroyer getProcessDestroyer();
+    boolean isFailure(final int exitValue);
 
     /**
-     * Gets the handler for cleanup of started processes if the main process
-     * is going to terminate.
+     * Define the {@code exitValue} of the process to be considered
+     * successful. If a different exit value is returned by
+     * the process then {@link org.apache.commons.exec.Executor#execute(CommandLine)}
+     * will throw an {@link org.apache.commons.exec.ExecuteException}
      *
-     * @param processDestroyer the ProcessDestroyer
+     * @param value the exit code representing successful execution
      */
-    void setProcessDestroyer(ProcessDestroyer processDestroyer);
+    void setExitValue(final int value);
 
     /**
-     * Gets the working directory of the created process.
+     * Define a list of {@code exitValue} of the process to be considered
+     * successful. The caller can pass one of the following values
+     * <ul>
+     *  <li>an array of exit values to be considered successful</li>
+     *  <li>an empty array for auto-detect of successful exit codes relying on {@link org.apache.commons.exec.Executor#isFailure(int)}</li>
+     *  <li>null to indicate to skip checking of exit codes</li>
+     * </ul>
      *
-     * @return the working directory
-     */
-    File getWorkingDirectory();
-
-    /**
-     * Sets the working directory of the created process. The
-     * working directory must exist when you start the process.
+     * If an undefined exit value is returned by the process then
+     * {@link org.apache.commons.exec.Executor#execute(CommandLine)}  will
+     * throw an {@link org.apache.commons.exec.ExecuteException}.
      *
-     * @param dir the working directory
+     * @param values a list of the exit codes
      */
-    void setWorkingDirectory(File dir);
+    void setExitValues(final int[] values);
 
     /**
-     * Methods for starting synchronous execution. The child process inherits
-     * all environment variables of the parent process.
+     * Gets the handler for cleanup of started processes if the main process
+     * is going to terminate.
      *
-     * @param command the command to execute
-     * @return process exit value
-     * @throws ExecuteException execution of subprocess failed or the
-     *          subprocess returned a exit value indicating a failure
-     *          {@link Executor#setExitValue(int)}.
+     * @param processDestroyer the ProcessDestroyer
      */
-    int execute(CommandLine command)
-        throws ExecuteException, IOException;
+    void setProcessDestroyer(ProcessDestroyer processDestroyer);
 
     /**
-     * Methods for starting synchronous execution.
+     * Sets a custom the StreamHandler used for providing
+     * input and retrieving the output. If you don't provide
+     * a proper stream handler the executed process might block
+     * when writing to stdout and/or stderr (see
+     * {@link Process Process}).
      *
-     * @param command the command to execute
-     * @param environment The environment for the new process. If null, the
-     *          environment of the current process is used.
-     * @return process exit value
-     * @throws ExecuteException execution of subprocess failed or the
-     *          subprocess returned a exit value indicating a failure
-     *          {@link Executor#setExitValue(int)}.
+     * @param streamHandler the stream handler
      */
-    int execute(CommandLine command, Map<String, String> environment)
-        throws ExecuteException, IOException;
+    void setStreamHandler(ExecuteStreamHandler streamHandler);
 
     /**
-     * Methods for starting asynchronous execution. The child process inherits
-     * all environment variables of the parent process. Result provided to
-     * callback handler.
+     * Sets the watchdog used to kill of processes running,
+     * typically, too long time.
      *
-     * @param command the command to execute
-     * @param handler capture process termination and exit code
-     * @throws ExecuteException execution of subprocess failed
+     * @param watchDog the watchdog
      */
-    void execute(CommandLine command, ExecuteResultHandler handler)
-        throws ExecuteException, IOException;
+    void setWatchdog(ExecuteWatchdog watchDog);
 
     /**
-     * Methods for starting asynchronous execution. The child process inherits
-     * all environment variables of the parent process. Result provided to
-     * callback handler.
+     * Sets the working directory of the created process. The
+     * working directory must exist when you start the process.
      *
-     * @param command the command to execute
-     * @param environment The environment for the new process. If null, the
-     *          environment of the current process is used.
-     * @param handler capture process termination and exit code
-     * @throws ExecuteException execution of subprocess failed
+     * @param dir the working directory
      */
-    void execute(CommandLine command, Map<String, String> environment, ExecuteResultHandler handler)
-        throws ExecuteException, IOException;
+    void setWorkingDirectory(File dir);
 }
diff --git a/src/main/java/org/apache/commons/exec/LogOutputStream.java b/src/main/java/org/apache/commons/exec/LogOutputStream.java
index 641ba080..35077564 100644
--- a/src/main/java/org/apache/commons/exec/LogOutputStream.java
+++ b/src/main/java/org/apache/commons/exec/LogOutputStream.java
@@ -89,23 +89,16 @@ public abstract class LogOutputStream
     }
 
     /**
-     * Write the data to the buffer and flush the buffer, if a line separator is
-     * detected.
+     * Writes all remaining data from the buffer.
      *
-     * @param cc data to log (byte).
-     * @see java.io.OutputStream#write(int)
+     * @see java.io.OutputStream#close()
      */
     @Override
-    public void write(final int cc) throws IOException {
-        final byte c = (byte) cc;
-        if (c == '\n' || c == '\r') {
-            if (!skip) {
-                processBuffer();
-            }
-        } else {
-            buffer.write(cc);
+    public void close() throws IOException {
+        if (buffer.size() > 0) {
+            processBuffer();
         }
-        skip = c == '\r';
+        super.close();
     }
 
     /**
@@ -121,25 +114,38 @@ public abstract class LogOutputStream
     }
 
     /**
-     * Writes all remaining data from the buffer.
-     *
-     * @see java.io.OutputStream#close()
+     * @return the trace level of the log system
      */
-    @Override
-    public void close() throws IOException {
-        if (buffer.size() > 0) {
-            processBuffer();
-        }
-        super.close();
+    public int getMessageLevel() {
+        return level;
     }
 
     /**
-     * @return the trace level of the log system
+     * Converts the buffer to a string and sends it to {@code processLine}.
      */
-    public int getMessageLevel() {
-        return level;
+    protected void processBuffer() {
+        processLine(buffer.toString(charset));
+        buffer.reset();
+    }
+
+    /**
+     * Logs a line to the log system of the user.
+     *
+     * @param line
+     *            the line to log.
+     */
+    protected void processLine(final String line) {
+        processLine(line, level);
     }
 
+    /**
+     * Logs a line to the log system of the user.
+     *
+     * @param line the line to log.
+     * @param logLevel the log level to use
+     */
+    protected abstract void processLine(final String line, final int logLevel);
+
     /**
      * Write a block of characters to the output stream
      *
@@ -176,28 +182,22 @@ public abstract class LogOutputStream
     }
 
     /**
-     * Converts the buffer to a string and sends it to {@code processLine}.
-     */
-    protected void processBuffer() {
-        processLine(buffer.toString(charset));
-        buffer.reset();
-    }
-
-    /**
-     * Logs a line to the log system of the user.
+     * Write the data to the buffer and flush the buffer, if a line separator is
+     * detected.
      *
-     * @param line
-     *            the line to log.
+     * @param cc data to log (byte).
+     * @see java.io.OutputStream#write(int)
      */
-    protected void processLine(final String line) {
-        processLine(line, level);
+    @Override
+    public void write(final int cc) throws IOException {
+        final byte c = (byte) cc;
+        if (c == '\n' || c == '\r') {
+            if (!skip) {
+                processBuffer();
+            }
+        } else {
+            buffer.write(cc);
+        }
+        skip = c == '\r';
     }
-
-    /**
-     * Logs a line to the log system of the user.
-     *
-     * @param line the line to log.
-     * @param logLevel the log level to use
-     */
-    protected abstract void processLine(final String line, final int logLevel);
 }
diff --git a/src/main/java/org/apache/commons/exec/OS.java b/src/main/java/org/apache/commons/exec/OS.java
index 3e0e8519..9023e3ea 100644
--- a/src/main/java/org/apache/commons/exec/OS.java
+++ b/src/main/java/org/apache/commons/exec/OS.java
@@ -83,9 +83,14 @@ public final class OS {
     private static final String PATH_SEP = File.pathSeparator;
 
     /**
-     * Default constructor
+     * Determines if the OS on which commons-exec is executing matches
+     * the given OS architecture.
+     *
+     * @param arch the OS architecture to check for
+     * @return true if the OS matches
      */
-    private OS() {
+    public static boolean isArch(final String arch) {
+        return isOs(null, null, arch, null);
     }
 
     /**
@@ -111,10 +116,18 @@ public final class OS {
         return isFamily(FAMILY_NETWARE);
     }
 
+    public static boolean isFamilyOpenVms() {
+        return isFamily(FAMILY_VMS);
+    }
+
     public static boolean isFamilyOS2() {
         return isFamily(FAMILY_OS2);
     }
 
+    public static boolean isFamilyOS400() {
+        return isFamily(FAMILY_OS400);
+    }
+
     public static boolean isFamilyTandem() {
         return isFamily(FAMILY_TANDEM);
     }
@@ -123,14 +136,14 @@ public final class OS {
         return isFamily(FAMILY_UNIX);
     }
 
-    public static boolean isFamilyWindows() {
-        return isFamily(FAMILY_WINDOWS);
-    }
-
     public static boolean isFamilyWin9x() {
         return isFamily(FAMILY_9X);
     }
 
+    public static boolean isFamilyWindows() {
+        return isFamily(FAMILY_WINDOWS);
+    }
+
     public static boolean isFamilyWinNT() {
         return isFamily(FAMILY_NT);
     }
@@ -139,14 +152,6 @@ public final class OS {
         return isFamily(FAMILY_ZOS);
     }
 
-    public static boolean isFamilyOS400() {
-        return isFamily(FAMILY_OS400);
-    }
-
-    public static boolean isFamilyOpenVms() {
-        return isFamily(FAMILY_VMS);
-    }
-
     /**
      * Determines if the OS on which commons-exec is executing matches
      * the given OS name.
@@ -158,28 +163,6 @@ public final class OS {
         return isOs(null, name, null, null);
     }
 
-    /**
-     * Determines if the OS on which commons-exec is executing matches
-     * the given OS architecture.
-     *
-     * @param arch the OS architecture to check for
-     * @return true if the OS matches
-     */
-    public static boolean isArch(final String arch) {
-        return isOs(null, null, arch, null);
-    }
-
-    /**
-     * Determines if the OS on which commonss-exec is executing matches
-     * the given OS version.
-     *
-     * @param version the OS version to check for
-     * @return true if the OS matches
-     */
-    public static boolean isVersion(final String version) {
-        return isOs(null, null, null, version);
-    }
-
     /**
      * Determines if the OS on which commons-exec is executing matches the
      * given OS family, name, architecture and version.
@@ -265,4 +248,21 @@ public final class OS {
         }
         return retValue;
     }
+
+    /**
+     * Determines if the OS on which commonss-exec is executing matches
+     * the given OS version.
+     *
+     * @param version the OS version to check for
+     * @return true if the OS matches
+     */
+    public static boolean isVersion(final String version) {
+        return isOs(null, null, null, version);
+    }
+
+    /**
+     * Default constructor
+     */
+    private OS() {
+    }
 }
diff --git a/src/main/java/org/apache/commons/exec/PumpStreamHandler.java b/src/main/java/org/apache/commons/exec/PumpStreamHandler.java
index 19e38080..aa3e8e4e 100644
--- a/src/main/java/org/apache/commons/exec/PumpStreamHandler.java
+++ b/src/main/java/org/apache/commons/exec/PumpStreamHandler.java
@@ -93,26 +93,84 @@ public class PumpStreamHandler implements ExecuteStreamHandler {
     }
 
     /**
-     * Sets maximum time to wait until output streams are exchausted
-     * when {@link #stop()} was called.
+     * Create the pump to handle error output.
      *
-     * @param timeout timeout in milliseconds or zero to wait forever (default)
+     * @param is the <CODE>InputStream</CODE>.
+     * @param os the <CODE>OutputStream</CODE>.
      */
-    public void setStopTimeout(final long timeout) {
-        this.stopTimeout = timeout;
+    protected void createProcessErrorPump(final InputStream is, final OutputStream os) {
+        errorThread = createPump(is, os);
     }
 
     /**
-     * Sets the <CODE>InputStream</CODE> from which to read the standard output
-     * of the process.
+     * Create the pump to handle process output.
      *
      * @param is the <CODE>InputStream</CODE>.
+     * @param os the <CODE>OutputStream</CODE>.
      */
-    @Override
-    public void setProcessOutputStream(final InputStream is) {
-        if (out != null) {
-            createProcessOutputPump(is, out);
-        }
+    protected void createProcessOutputPump(final InputStream is, final OutputStream os) {
+        outputThread = createPump(is, os);
+    }
+
+    /**
+     * Creates a stream pumper to copy the given input stream to the given
+     * output stream. When the 'os' is an PipedOutputStream we are closing
+     * 'os' afterwards to avoid an IOException ("Write end dead").
+     *
+     * @param is the input stream to copy from
+     * @param os the output stream to copy into
+     * @return the stream pumper thread
+     */
+    protected Thread createPump(final InputStream is, final OutputStream os) {
+        return createPump(is, os, os instanceof PipedOutputStream);
+    }
+
+    /**
+     * Creates a stream pumper to copy the given input stream to the given
+     * output stream.
+     *
+     * @param is                 the input stream to copy from
+     * @param os                 the output stream to copy into
+     * @param closeWhenExhausted close the output stream when the input stream is exhausted
+     * @return the stream pumper thread
+     */
+    protected Thread createPump(final InputStream is, final OutputStream os, final boolean closeWhenExhausted) {
+        final Thread result = new Thread(new StreamPumper(is, os, closeWhenExhausted), "Exec Stream Pumper");
+        result.setDaemon(true);
+        return result;
+    }
+
+    /**
+     * Creates a stream pumper to copy the given input stream to the given
+     * output stream.
+     *
+     * @param is the System.in input stream to copy from
+     * @param os the output stream to copy into
+     * @return the stream pumper thread
+     */
+    private Thread createSystemInPump(final InputStream is, final OutputStream os) {
+        inputStreamPumper = new InputStreamPumper(is, os);
+        final Thread result = new Thread(inputStreamPumper, "Exec Input Stream Pumper");
+        result.setDaemon(true);
+        return result;
+    }
+
+    /**
+     * Gets the error stream.
+     *
+     * @return <CODE>OutputStream</CODE>.
+     */
+    protected OutputStream getErr() {
+        return err;
+    }
+
+    /**
+     * Gets the output stream.
+     *
+     * @return <CODE>OutputStream</CODE>.
+     */
+    protected OutputStream getOut() {
+        return out;
     }
 
     /**
@@ -152,6 +210,29 @@ public class PumpStreamHandler implements ExecuteStreamHandler {
         }
     }
 
+    /**
+     * Sets the <CODE>InputStream</CODE> from which to read the standard output
+     * of the process.
+     *
+     * @param is the <CODE>InputStream</CODE>.
+     */
+    @Override
+    public void setProcessOutputStream(final InputStream is) {
+        if (out != null) {
+            createProcessOutputPump(is, out);
+        }
+    }
+
+    /**
+     * Sets maximum time to wait until output streams are exchausted
+     * when {@link #stop()} was called.
+     *
+     * @param timeout timeout in milliseconds or zero to wait forever (default)
+     */
+    public void setStopTimeout(final long timeout) {
+        this.stopTimeout = timeout;
+    }
+
     /**
      * Start the <CODE>Thread</CODE>s.
      */
@@ -206,72 +287,6 @@ public class PumpStreamHandler implements ExecuteStreamHandler {
         }
     }
 
-    /**
-     * Gets the error stream.
-     *
-     * @return <CODE>OutputStream</CODE>.
-     */
-    protected OutputStream getErr() {
-        return err;
-    }
-
-    /**
-     * Gets the output stream.
-     *
-     * @return <CODE>OutputStream</CODE>.
-     */
-    protected OutputStream getOut() {
-        return out;
-    }
-
-    /**
-     * Create the pump to handle process output.
-     *
-     * @param is the <CODE>InputStream</CODE>.
-     * @param os the <CODE>OutputStream</CODE>.
-     */
-    protected void createProcessOutputPump(final InputStream is, final OutputStream os) {
-        outputThread = createPump(is, os);
-    }
-
-    /**
-     * Create the pump to handle error output.
-     *
-     * @param is the <CODE>InputStream</CODE>.
-     * @param os the <CODE>OutputStream</CODE>.
-     */
-    protected void createProcessErrorPump(final InputStream is, final OutputStream os) {
-        errorThread = createPump(is, os);
-    }
-
-    /**
-     * Creates a stream pumper to copy the given input stream to the given
-     * output stream. When the 'os' is an PipedOutputStream we are closing
-     * 'os' afterwards to avoid an IOException ("Write end dead").
-     *
-     * @param is the input stream to copy from
-     * @param os the output stream to copy into
-     * @return the stream pumper thread
-     */
-    protected Thread createPump(final InputStream is, final OutputStream os) {
-        return createPump(is, os, os instanceof PipedOutputStream);
-    }
-
-    /**
-     * Creates a stream pumper to copy the given input stream to the given
-     * output stream.
-     *
-     * @param is                 the input stream to copy from
-     * @param os                 the output stream to copy into
-     * @param closeWhenExhausted close the output stream when the input stream is exhausted
-     * @return the stream pumper thread
-     */
-    protected Thread createPump(final InputStream is, final OutputStream os, final boolean closeWhenExhausted) {
-        final Thread result = new Thread(new StreamPumper(is, os, closeWhenExhausted), "Exec Stream Pumper");
-        result.setDaemon(true);
-        return result;
-    }
-
     /**
      * Stopping a pumper thread. The implementation actually waits
      * longer than specified in 'timeout' to detect if the timeout
@@ -301,19 +316,4 @@ public class PumpStreamHandler implements ExecuteStreamHandler {
             }
         }
     }
-
-    /**
-     * Creates a stream pumper to copy the given input stream to the given
-     * output stream.
-     *
-     * @param is the System.in input stream to copy from
-     * @param os the output stream to copy into
-     * @return the stream pumper thread
-     */
-    private Thread createSystemInPump(final InputStream is, final OutputStream os) {
-        inputStreamPumper = new InputStreamPumper(is, os);
-        final Thread result = new Thread(inputStreamPumper, "Exec Input Stream Pumper");
-        result.setDaemon(true);
-        return result;
-    }
 }
diff --git a/src/main/java/org/apache/commons/exec/StreamPumper.java b/src/main/java/org/apache/commons/exec/StreamPumper.java
index 8a4ee161..505f5828 100644
--- a/src/main/java/org/apache/commons/exec/StreamPumper.java
+++ b/src/main/java/org/apache/commons/exec/StreamPumper.java
@@ -46,6 +46,16 @@ public class StreamPumper implements Runnable {
     /** close the output stream when exhausted */
     private final boolean closeWhenExhausted;
 
+    /**
+     * Create a new stream pumper.
+     *
+     * @param is input stream to read data from
+     * @param os output stream to write data to.
+     */
+    public StreamPumper(final InputStream is, final OutputStream os) {
+        this(is, os, false);
+    }
+
     /**
      * Create a new stream pumper.
      *
@@ -78,13 +88,12 @@ public class StreamPumper implements Runnable {
     }
 
     /**
-     * Create a new stream pumper.
+     * Tells whether the end of the stream has been reached.
      *
-     * @param is input stream to read data from
-     * @param os output stream to write data to.
+     * @return true is the stream has been exhausted.
      */
-    public StreamPumper(final InputStream is, final OutputStream os) {
-        this(is, os, false);
+    public synchronized boolean isFinished() {
+        return finished;
     }
 
     /**
@@ -123,15 +132,6 @@ public class StreamPumper implements Runnable {
         }
     }
 
-    /**
-     * Tells whether the end of the stream has been reached.
-     *
-     * @return true is the stream has been exhausted.
-     */
-    public synchronized boolean isFinished() {
-        return finished;
-    }
-
     /**
      * This method blocks until the stream pumper finishes.
      *
diff --git a/src/main/java/org/apache/commons/exec/Watchdog.java b/src/main/java/org/apache/commons/exec/Watchdog.java
index 978e6246..dc892307 100644
--- a/src/main/java/org/apache/commons/exec/Watchdog.java
+++ b/src/main/java/org/apache/commons/exec/Watchdog.java
@@ -44,10 +44,6 @@ public class Watchdog implements Runnable {
         observers.addElement(to);
     }
 
-    public void removeTimeoutObserver(final TimeoutObserver to) {
-        observers.removeElement(to);
-    }
-
     protected final void fireTimeoutOccured() {
         final Enumeration<TimeoutObserver> e = observers.elements();
         while (e.hasMoreElements()) {
@@ -55,16 +51,8 @@ public class Watchdog implements Runnable {
         }
     }
 
-    public synchronized void start() {
-        stopped = false;
-        final Thread t = new Thread(this, "WATCHDOG");
-        t.setDaemon(true);
-        t.start();
-    }
-
-    public synchronized void stop() {
-        stopped = true;
-        notifyAll();
+    public void removeTimeoutObserver(final TimeoutObserver to) {
+        observers.removeElement(to);
     }
 
     @Override
@@ -90,4 +78,16 @@ public class Watchdog implements Runnable {
         }
     }
 
+    public synchronized void start() {
+        stopped = false;
+        final Thread t = new Thread(this, "WATCHDOG");
+        t.setDaemon(true);
+        t.start();
+    }
+
+    public synchronized void stop() {
+        stopped = true;
+        notifyAll();
+    }
+
 }
diff --git a/src/main/java/org/apache/commons/exec/environment/DefaultProcessingEnvironment.java b/src/main/java/org/apache/commons/exec/environment/DefaultProcessingEnvironment.java
index 08111ff7..a09fc262 100644
--- a/src/main/java/org/apache/commons/exec/environment/DefaultProcessingEnvironment.java
+++ b/src/main/java/org/apache/commons/exec/environment/DefaultProcessingEnvironment.java
@@ -42,23 +42,18 @@ public class DefaultProcessingEnvironment {
     protected Map<String, String> procEnvironment;
 
     /**
-     * Find the list of environment variables for this process.
+     * Creates a map that obeys the casing rules of the current platform for key
+     * lookup. E.g. on a Windows platform, the map keys will be
+     * case-insensitive.
      *
-     * @return a map containing the environment variables
-     * @throws IOException obtaining the environment variables failed
+     * @return The map for storage of environment variables, never
+     *         {@code null}.
      */
-    public synchronized Map<String, String> getProcEnvironment() throws IOException {
-
-        if (procEnvironment == null) {
-            procEnvironment = this.createProcEnvironment();
+    private Map<String, String> createEnvironmentMap() {
+        if (OS.isFamilyWindows()) {
+            return new TreeMap<>(String::compareToIgnoreCase);
         }
-
-        // create a copy of the map just in case that
-        // anyone is going to modifiy it, e.g. removing
-        // or setting an evironment variable
-        final Map<String, String> copy = createEnvironmentMap();
-        copy.putAll(procEnvironment);
-        return copy;
+        return new HashMap<>();
     }
 
     /**
@@ -106,24 +101,6 @@ public class DefaultProcessingEnvironment {
         return procEnvironment;
     }
 
-    /**
-     * Start a process to list the environment variables.
-     *
-     * @return a reader containing the output of the process
-     * @throws IOException starting the process failed
-     * @deprecated No longer needed
-     */
-    @Deprecated
-    protected BufferedReader runProcEnvCommand() throws IOException {
-//        final ByteArrayOutputStream out = new ByteArrayOutputStream();
-//        final Executor exe = new DefaultExecutor();
-//        exe.setStreamHandler(new PumpStreamHandler(out));
-//        // ignore the exit value - Just try to use what we got
-//        exe.execute(getProcEnvCommand());
-//        return new BufferedReader(new StringReader(toString(out)));
-        return null;
-    }
-
     /**
      * Determine the OS specific command line to get a list of environment
      * variables.
@@ -178,6 +155,26 @@ public class DefaultProcessingEnvironment {
         return commandLine;
     }
 
+    /**
+     * Find the list of environment variables for this process.
+     *
+     * @return a map containing the environment variables
+     * @throws IOException obtaining the environment variables failed
+     */
+    public synchronized Map<String, String> getProcEnvironment() throws IOException {
+
+        if (procEnvironment == null) {
+            procEnvironment = this.createProcEnvironment();
+        }
+
+        // create a copy of the map just in case that
+        // anyone is going to modifiy it, e.g. removing
+        // or setting an evironment variable
+        final Map<String, String> copy = createEnvironmentMap();
+        copy.putAll(procEnvironment);
+        return copy;
+    }
+
 //    /**
 //     * ByteArrayOutputStream#toString doesn't seem to work reliably on OS/390,
 //     * at least not the way we use it in the execution context.
@@ -205,18 +202,21 @@ public class DefaultProcessingEnvironment {
 //    }
 
     /**
-     * Creates a map that obeys the casing rules of the current platform for key
-     * lookup. E.g. on a Windows platform, the map keys will be
-     * case-insensitive.
+     * Start a process to list the environment variables.
      *
-     * @return The map for storage of environment variables, never
-     *         {@code null}.
+     * @return a reader containing the output of the process
+     * @throws IOException starting the process failed
+     * @deprecated No longer needed
      */
-    private Map<String, String> createEnvironmentMap() {
-        if (OS.isFamilyWindows()) {
-            return new TreeMap<>(String::compareToIgnoreCase);
-        }
-        return new HashMap<>();
+    @Deprecated
+    protected BufferedReader runProcEnvCommand() throws IOException {
+//        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+//        final Executor exe = new DefaultExecutor();
+//        exe.setStreamHandler(new PumpStreamHandler(out));
+//        // ignore the exit value - Just try to use what we got
+//        exe.execute(getProcEnvCommand());
+//        return new BufferedReader(new StringReader(toString(out)));
+        return null;
     }
 
 }
diff --git a/src/main/java/org/apache/commons/exec/environment/EnvironmentUtils.java b/src/main/java/org/apache/commons/exec/environment/EnvironmentUtils.java
index edb26023..e0dda196 100644
--- a/src/main/java/org/apache/commons/exec/environment/EnvironmentUtils.java
+++ b/src/main/java/org/apache/commons/exec/environment/EnvironmentUtils.java
@@ -40,32 +40,15 @@ public class EnvironmentUtils
     }
 
     /**
-     * Disable constructor.
-     */
-    private EnvironmentUtils() {
-
-    }
-
-    /**
-     * Gets the variable list as an array.
+     * Add a key/value pair to the given environment.
+     * If the key matches an existing key, the previous setting is replaced.
      *
-     * @param environment the environment to use, may be {@code null}
-     * @return array of key=value assignment strings or {@code null} if and only if
-     *     the input map was {@code null}
+     * @param environment the current environment
+     * @param keyAndValue the key/value pair
      */
-    public static String[] toStrings(final Map<String, String> environment) {
-        if (environment == null) {
-            return null;
-        }
-        final String[] result = new String[environment.size()];
-        int i = 0;
-        for (final Entry<String, String> entry : environment.entrySet()) {
-            final String key  = entry.getKey() == null ? "" : entry.getKey().toString();
-            final String value = entry.getValue() == null ? "" : entry.getValue().toString();
-            result[i] = key + "=" + value;
-            i++;
-        }
-        return result;
+    public static void addVariableToEnvironment(final Map<String, String> environment, final String keyAndValue) {
+        final String[] parsedVariable = parseEnvironmentVariable(keyAndValue);
+        environment.put(parsedVariable[0], parsedVariable[1]);
     }
 
     /**
@@ -81,18 +64,6 @@ public class EnvironmentUtils
         return PROCESSING_ENVIRONMENT_IMPLEMENTATION.getProcEnvironment();
     }
 
-    /**
-     * Add a key/value pair to the given environment.
-     * If the key matches an existing key, the previous setting is replaced.
-     *
-     * @param environment the current environment
-     * @param keyAndValue the key/value pair
-     */
-    public static void addVariableToEnvironment(final Map<String, String> environment, final String keyAndValue) {
-        final String[] parsedVariable = parseEnvironmentVariable(keyAndValue);
-        environment.put(parsedVariable[0], parsedVariable[1]);
-    }
-
     /**
      * Split a key/value pair into a String[]. It is assumed
      * that the ky/value pair contains a '=' character.
@@ -115,4 +86,33 @@ public class EnvironmentUtils
         return result;
     }
 
+    /**
+     * Gets the variable list as an array.
+     *
+     * @param environment the environment to use, may be {@code null}
+     * @return array of key=value assignment strings or {@code null} if and only if
+     *     the input map was {@code null}
+     */
+    public static String[] toStrings(final Map<String, String> environment) {
+        if (environment == null) {
+            return null;
+        }
+        final String[] result = new String[environment.size()];
+        int i = 0;
+        for (final Entry<String, String> entry : environment.entrySet()) {
+            final String key  = entry.getKey() == null ? "" : entry.getKey().toString();
+            final String value = entry.getValue() == null ? "" : entry.getValue().toString();
+            result[i] = key + "=" + value;
+            i++;
+        }
+        return result;
+    }
+
+    /**
+     * Disable constructor.
+     */
+    private EnvironmentUtils() {
+
+    }
+
 }
diff --git a/src/main/java/org/apache/commons/exec/launcher/CommandLauncherFactory.java b/src/main/java/org/apache/commons/exec/launcher/CommandLauncherFactory.java
index 5befe80c..9f2bf34e 100644
--- a/src/main/java/org/apache/commons/exec/launcher/CommandLauncherFactory.java
+++ b/src/main/java/org/apache/commons/exec/launcher/CommandLauncherFactory.java
@@ -24,9 +24,6 @@ import org.apache.commons.exec.OS;
  */
 public final class CommandLauncherFactory {
 
-    private CommandLauncherFactory() {
-    }
-
     /**
      * Factory method to create an appropriate launcher.
      *
@@ -44,4 +41,7 @@ public final class CommandLauncherFactory {
 
         return launcher;
     }
+
+    private CommandLauncherFactory() {
+    }
 }
diff --git a/src/main/java/org/apache/commons/exec/launcher/CommandLauncherProxy.java b/src/main/java/org/apache/commons/exec/launcher/CommandLauncherProxy.java
index 9370149e..caf9b52e 100644
--- a/src/main/java/org/apache/commons/exec/launcher/CommandLauncherProxy.java
+++ b/src/main/java/org/apache/commons/exec/launcher/CommandLauncherProxy.java
@@ -28,12 +28,12 @@ import org.apache.commons.exec.CommandLine;
  */
 public abstract class CommandLauncherProxy extends CommandLauncherImpl {
 
+    private final CommandLauncher myLauncher;
+
     public CommandLauncherProxy(final CommandLauncher launcher) {
         myLauncher = launcher;
     }
 
-    private final CommandLauncher myLauncher;
-
     /**
      * Launches the given command in a new process. Delegates this method to the
      * proxied launcher
diff --git a/src/main/java/org/apache/commons/exec/launcher/VmsCommandLauncher.java b/src/main/java/org/apache/commons/exec/launcher/VmsCommandLauncher.java
index bf8f47b0..3f844ca3 100644
--- a/src/main/java/org/apache/commons/exec/launcher/VmsCommandLauncher.java
+++ b/src/main/java/org/apache/commons/exec/launcher/VmsCommandLauncher.java
@@ -37,30 +37,6 @@ import org.apache.commons.exec.util.StringUtils;
  */
 public class VmsCommandLauncher extends Java13CommandLauncher {
 
-    /**
-     * Launches the given command in a new process.
-     */
-    @Override
-    public Process exec(final CommandLine cmd, final Map<String, String> env) throws IOException {
-        return super.exec(new CommandLine(createCommandFile(cmd, env).getPath()), env);
-    }
-
-    /**
-     * Launches the given command in a new process, in the given working directory. Note that under Java 1.3.1, 1.4.0 and 1.4.1 on VMS this method only works if
-     * {@code workingDir} is null or the logical JAVA$FORK_SUPPORT_CHDIR needs to be set to TRUE.
-     */
-    @Override
-    public Process exec(final CommandLine cmd, final Map<String, String> env, final File workingDir) throws IOException {
-        return super.exec(new CommandLine(createCommandFile(cmd, env).getPath()), env, workingDir);
-    }
-
-    /** @see org.apache.commons.exec.launcher.CommandLauncher#isFailure(int) */
-    @Override
-    public boolean isFailure(final int exitValue) {
-        // even exit value signals failure
-        return exitValue % 2 == 0;
-    }
-
     /*
      * Writes the command into a temporary DCL script and returns the
      * corresponding File object. The script will be deleted on exit.
@@ -126,4 +102,28 @@ public class VmsCommandLauncher extends Java13CommandLauncher {
         }
         return script;
     }
+
+    /**
+     * Launches the given command in a new process.
+     */
+    @Override
+    public Process exec(final CommandLine cmd, final Map<String, String> env) throws IOException {
+        return super.exec(new CommandLine(createCommandFile(cmd, env).getPath()), env);
+    }
+
+    /**
+     * Launches the given command in a new process, in the given working directory. Note that under Java 1.3.1, 1.4.0 and 1.4.1 on VMS this method only works if
+     * {@code workingDir} is null or the logical JAVA$FORK_SUPPORT_CHDIR needs to be set to TRUE.
+     */
+    @Override
+    public Process exec(final CommandLine cmd, final Map<String, String> env, final File workingDir) throws IOException {
+        return super.exec(new CommandLine(createCommandFile(cmd, env).getPath()), env, workingDir);
+    }
+
+    /** @see org.apache.commons.exec.launcher.CommandLauncher#isFailure(int) */
+    @Override
+    public boolean isFailure(final int exitValue) {
+        // even exit value signals failure
+        return exitValue % 2 == 0;
+    }
 }
diff --git a/src/main/java/org/apache/commons/exec/util/MapUtils.java b/src/main/java/org/apache/commons/exec/util/MapUtils.java
index eab30778..77e53bbb 100644
--- a/src/main/java/org/apache/commons/exec/util/MapUtils.java
+++ b/src/main/java/org/apache/commons/exec/util/MapUtils.java
@@ -41,37 +41,6 @@ public class MapUtils
         return source == null ? null : new HashMap<>(source);
     }
 
-    /**
-     * Clones a map and prefixes the keys in the clone, e.g. for mapping "JAVA_HOME" to "env.JAVA_HOME" to simulate the
-     * behavior of Ant.
-     *
-     * @param source
-     *            the source map
-     * @param prefix
-     *            the prefix used for all names
-     * @param <K>
-     *            the map key type
-     * @param <V>
-     *            the map value type
-     * @return the clone of the source map
-     */
-    public static <K, V> Map<String, V> prefix(final Map<K, V> source, final String prefix) {
-
-        if (source == null) {
-            return null;
-        }
-
-        final Map<String, V> result = new HashMap<>();
-
-        for (final Map.Entry<K, V> entry : source.entrySet()) {
-            final K key = entry.getKey();
-            final V value = entry.getValue();
-            result.put(prefix + '.' + key.toString(), value);
-        }
-
-        return result;
-    }
-
     /**
      * Clones the lhs map and add all things from the rhs map.
      *
@@ -102,4 +71,35 @@ public class MapUtils
 
         return result;
     }
+
+    /**
+     * Clones a map and prefixes the keys in the clone, e.g. for mapping "JAVA_HOME" to "env.JAVA_HOME" to simulate the
+     * behavior of Ant.
+     *
+     * @param source
+     *            the source map
+     * @param prefix
+     *            the prefix used for all names
+     * @param <K>
+     *            the map key type
+     * @param <V>
+     *            the map value type
+     * @return the clone of the source map
+     */
+    public static <K, V> Map<String, V> prefix(final Map<K, V> source, final String prefix) {
+
+        if (source == null) {
+            return null;
+        }
+
+        final Map<String, V> result = new HashMap<>();
+
+        for (final Map.Entry<K, V> entry : source.entrySet()) {
+            final K key = entry.getKey();
+            final V value = entry.getValue();
+            result.put(prefix + '.' + key.toString(), value);
+        }
+
+        return result;
+    }
 }
diff --git a/src/main/java/org/apache/commons/exec/util/StringUtils.java b/src/main/java/org/apache/commons/exec/util/StringUtils.java
index 9bcb8134..a3a8be2d 100644
--- a/src/main/java/org/apache/commons/exec/util/StringUtils.java
+++ b/src/main/java/org/apache/commons/exec/util/StringUtils.java
@@ -39,6 +39,94 @@ public class StringUtils {
     private static final char SLASH_CHAR = '/';
     private static final char BACKSLASH_CHAR = '\\';
 
+    /**
+     * Fixes the file separator char for the target platform
+     * using the following replacement.
+     *
+     * <ul>
+     *  <li>'/' &#x2192; File.separatorChar</li>
+     *  <li>'\\' &#x2192; File.separatorChar</li>
+     * </ul>
+     *
+     * @param arg the argument to fix
+     * @return the transformed argument
+     */
+    public static String fixFileSeparatorChar(final String arg) {
+        return arg.replace(SLASH_CHAR, File.separatorChar).replace(
+                BACKSLASH_CHAR, File.separatorChar);
+    }
+
+    /**
+     * Determines if this is a quoted argument - either single or
+     * double quoted.
+     *
+     * @param argument the argument to check
+     * @return true when the argument is quoted
+     */
+    public static boolean isQuoted(final String argument) {
+        return argument.startsWith(SINGLE_QUOTE) && argument.endsWith(SINGLE_QUOTE)
+                || argument.startsWith(DOUBLE_QUOTE) && argument.endsWith(DOUBLE_QUOTE);
+    }
+
+    /**
+     * Put quotes around the given String if necessary.
+     * <p>
+     * If the argument doesn't include spaces or quotes, return it as is. If it
+     * contains double quotes, use single quotes - else surround the argument by
+     * double quotes.
+     * </p>
+     *
+     * @param argument the argument to be quoted
+     * @return the quoted argument
+     * @throws IllegalArgumentException If argument contains both types of quotes
+     */
+    public static String quoteArgument(final String argument) {
+
+        String cleanedArgument = argument.trim();
+
+        // strip the quotes from both ends
+        while (cleanedArgument.startsWith(SINGLE_QUOTE) || cleanedArgument.startsWith(DOUBLE_QUOTE)) {
+            cleanedArgument = cleanedArgument.substring(1);
+        }
+
+        while (cleanedArgument.endsWith(SINGLE_QUOTE) || cleanedArgument.endsWith(DOUBLE_QUOTE)) {
+            cleanedArgument = cleanedArgument.substring(0, cleanedArgument.length() - 1);
+        }
+
+        final StringBuilder buf = new StringBuilder();
+        if (cleanedArgument.indexOf(DOUBLE_QUOTE) > -1) {
+            if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1) {
+                throw new IllegalArgumentException(
+                        "Can't handle single and double quotes in same argument");
+            }
+            return buf.append(SINGLE_QUOTE).append(cleanedArgument).append(
+                    SINGLE_QUOTE).toString();
+        }
+        if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1
+                || cleanedArgument.indexOf(" ") > -1) {
+            return buf.append(DOUBLE_QUOTE).append(cleanedArgument).append(
+                    DOUBLE_QUOTE).toString();
+        }
+        return cleanedArgument;
+    }
+
+    /**
+     * Split a string into an array of strings based
+     * on a separator.
+     *
+     * @param input     what to split
+     * @param splitChar what to split on
+     * @return the array of strings
+     */
+    public static String[] split(final String input, final String splitChar) {
+        final StringTokenizer tokens = new StringTokenizer(input, splitChar);
+        final List<String> strList = new ArrayList<>();
+        while (tokens.hasMoreTokens()) {
+            strList.add(tokens.nextToken());
+        }
+        return strList.toArray(EMPTY_STRING_ARRAY);
+    }
+
     /**
      * Perform a series of substitutions.
      * <p>
@@ -147,40 +235,6 @@ public class StringUtils {
         return argBuf;
     }
 
-    /**
-     * Split a string into an array of strings based
-     * on a separator.
-     *
-     * @param input     what to split
-     * @param splitChar what to split on
-     * @return the array of strings
-     */
-    public static String[] split(final String input, final String splitChar) {
-        final StringTokenizer tokens = new StringTokenizer(input, splitChar);
-        final List<String> strList = new ArrayList<>();
-        while (tokens.hasMoreTokens()) {
-            strList.add(tokens.nextToken());
-        }
-        return strList.toArray(EMPTY_STRING_ARRAY);
-    }
-
-    /**
-     * Fixes the file separator char for the target platform
-     * using the following replacement.
-     *
-     * <ul>
-     *  <li>'/' &#x2192; File.separatorChar</li>
-     *  <li>'\\' &#x2192; File.separatorChar</li>
-     * </ul>
-     *
-     * @param arg the argument to fix
-     * @return the transformed argument
-     */
-    public static String fixFileSeparatorChar(final String arg) {
-        return arg.replace(SLASH_CHAR, File.separatorChar).replace(
-                BACKSLASH_CHAR, File.separatorChar);
-    }
-
     /**
      * Concatenates an array of string using a separator.
      *
@@ -198,58 +252,4 @@ public class StringUtils {
         }
         return sb.toString();
     }
-
-    /**
-     * Put quotes around the given String if necessary.
-     * <p>
-     * If the argument doesn't include spaces or quotes, return it as is. If it
-     * contains double quotes, use single quotes - else surround the argument by
-     * double quotes.
-     * </p>
-     *
-     * @param argument the argument to be quoted
-     * @return the quoted argument
-     * @throws IllegalArgumentException If argument contains both types of quotes
-     */
-    public static String quoteArgument(final String argument) {
-
-        String cleanedArgument = argument.trim();
-
-        // strip the quotes from both ends
-        while (cleanedArgument.startsWith(SINGLE_QUOTE) || cleanedArgument.startsWith(DOUBLE_QUOTE)) {
-            cleanedArgument = cleanedArgument.substring(1);
-        }
-
-        while (cleanedArgument.endsWith(SINGLE_QUOTE) || cleanedArgument.endsWith(DOUBLE_QUOTE)) {
-            cleanedArgument = cleanedArgument.substring(0, cleanedArgument.length() - 1);
-        }
-
-        final StringBuilder buf = new StringBuilder();
-        if (cleanedArgument.indexOf(DOUBLE_QUOTE) > -1) {
-            if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1) {
-                throw new IllegalArgumentException(
-                        "Can't handle single and double quotes in same argument");
-            }
-            return buf.append(SINGLE_QUOTE).append(cleanedArgument).append(
-                    SINGLE_QUOTE).toString();
-        }
-        if (cleanedArgument.indexOf(SINGLE_QUOTE) > -1
-                || cleanedArgument.indexOf(" ") > -1) {
-            return buf.append(DOUBLE_QUOTE).append(cleanedArgument).append(
-                    DOUBLE_QUOTE).toString();
-        }
-        return cleanedArgument;
-    }
-
-    /**
-     * Determines if this is a quoted argument - either single or
-     * double quoted.
-     *
-     * @param argument the argument to check
-     * @return true when the argument is quoted
-     */
-    public static boolean isQuoted(final String argument) {
-        return argument.startsWith(SINGLE_QUOTE) && argument.endsWith(SINGLE_QUOTE)
-                || argument.startsWith(DOUBLE_QUOTE) && argument.endsWith(DOUBLE_QUOTE);
-    }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/exec/AbstractExecTest.java b/src/test/java/org/apache/commons/exec/AbstractExecTest.java
index 75aceac8..bf417375 100644
--- a/src/test/java/org/apache/commons/exec/AbstractExecTest.java
+++ b/src/test/java/org/apache/commons/exec/AbstractExecTest.java
@@ -33,6 +33,13 @@ public abstract class AbstractExecTest {
 
     @Rule public TestName name = new TestName();
 
+    /**
+     * Gets the name of the currently executed test.
+     */
+    protected String getName() {
+        return name.getMethodName();
+    }
+
     /**
      * Resolve the OS-specific test file to execute.
      */
@@ -55,11 +62,10 @@ public abstract class AbstractExecTest {
         return result;
     }
 
-    /**
-     * Gets the name of the currently executed test.
-     */
-    protected String getName() {
-        return name.getMethodName();
+    protected String testIsBrokenForCurrentOperatingSystem() {
+        final String msg = String.format("The test '%s' is broken for OS : %s", name.getMethodName(), OS_NAME);
+        System.err.println(msg);
+        return msg;
     }
 
     protected String testNotSupportedForCurrentOperatingSystem() {
@@ -68,10 +74,4 @@ public abstract class AbstractExecTest {
         return msg;
     }
 
-    protected String testIsBrokenForCurrentOperatingSystem() {
-        final String msg = String.format("The test '%s' is broken for OS : %s", name.getMethodName(), OS_NAME);
-        System.err.println(msg);
-        return msg;
-    }
-
 }
diff --git a/src/test/java/org/apache/commons/exec/CommandLineTest.java b/src/test/java/org/apache/commons/exec/CommandLineTest.java
index c016b6ab..c0505819 100644
--- a/src/test/java/org/apache/commons/exec/CommandLineTest.java
+++ b/src/test/java/org/apache/commons/exec/CommandLineTest.java
@@ -34,70 +34,66 @@ import org.junit.Test;
 public class CommandLineTest {
 
     @Test
-    public void testExecutable() {
+    public void testAddArgument() {
         final CommandLine cmdl = new CommandLine("test");
-        assertEquals("[test]", cmdl.toString());
-        assertArrayEquals(new String[]{"test"}, cmdl.toStrings());
-        assertEquals("test", cmdl.getExecutable());
-        assertTrue(cmdl.getArguments().length == 0);
+
+        cmdl.addArgument("foo");
+        cmdl.addArgument("bar");
+        assertEquals("[test, foo, bar]", cmdl.toString());
+        assertArrayEquals(new String[]{"test", "foo", "bar"}, cmdl.toStrings());
     }
 
     @Test
-    public void testExecutableZeroLengthString() {
-        try {
-            new CommandLine("");
-            fail("Must throw IllegalArgumentException");
-        } catch (final IllegalArgumentException e) {
-            // Expected
-        }
+    public void testAddArguments() {
+        final CommandLine cmdl = new CommandLine("test");
+        cmdl.addArguments("foo bar");
+        assertEquals("[test, foo, bar]", cmdl.toString());
+        assertArrayEquals(new String[]{"test", "foo", "bar"}, cmdl.toStrings());
     }
 
     @Test
-    public void testExecutableWhitespaceString() {
-        try {
-            new CommandLine("   ");
-            fail("Must throw IllegalArgumentException");
-        } catch (final IllegalArgumentException e) {
-            // Expected
-        }
+    public void testAddArgumentsArray() {
+        final CommandLine cmdl = new CommandLine("test");
+        cmdl.addArguments(new String[] {"foo", "bar"});
+        assertEquals("[test, foo, bar]", cmdl.toString());
+        assertArrayEquals(new String[]{"test", "foo", "bar"}, cmdl.toStrings());
     }
 
     @Test
-    public void testNullExecutable() {
-        try {
-            new CommandLine((String)null);
-            fail("Must throw IllegalArgumentException");
-        } catch (final IllegalArgumentException e) {
-            // Expected
-        }
+    public void testAddArgumentsArrayNull() {
+        final CommandLine cmdl = new CommandLine("test");
+        cmdl.addArguments((String[]) null);
+        assertEquals("[test]", cmdl.toString());
+        assertArrayEquals(new String[]{"test"}, cmdl.toStrings());
     }
 
     @Test
-    public void testAddArgument() {
+    public void testAddArgumentsWithQuotes() {
         final CommandLine cmdl = new CommandLine("test");
-
-        cmdl.addArgument("foo");
-        cmdl.addArgument("bar");
+        cmdl.addArguments("'foo' \"bar\"");
         assertEquals("[test, foo, bar]", cmdl.toString());
         assertArrayEquals(new String[]{"test", "foo", "bar"}, cmdl.toStrings());
     }
 
     @Test
-    public void testAddNullArgument() {
+    public void testAddArgumentsWithQuotesAndSpaces() {
         final CommandLine cmdl = new CommandLine("test");
-
-        cmdl.addArgument(null);
-        assertEquals("[test]", cmdl.toString());
-        assertArrayEquals(new String[]{"test"}, cmdl.toStrings());
+        cmdl.addArguments("'fo o' \"ba r\"");
+        assertEquals("[test, \"fo o\", \"ba r\"]", cmdl.toString());
+        assertArrayEquals(new String[]{"test", "\"fo o\"", "\"ba r\""}, cmdl
+                .toStrings());
     }
 
     @Test
-    public void testAddArgumentWithSpace() {
+    public void testAddArgumentWithBothQuotes() {
         final CommandLine cmdl = new CommandLine("test");
-        cmdl.addArgument("foo");
-        cmdl.addArgument("ba r");
-        assertEquals("[test, foo, \"ba r\"]", cmdl.toString());
-        assertArrayEquals(new String[]{"test", "foo", "\"ba r\""}, cmdl.toStrings());
+
+        try {
+            cmdl.addArgument("b\"a'r");
+            fail("IllegalArgumentException should be thrown");
+        } catch (final IllegalArgumentException e) {
+            // OK, expected
+        }
     }
 
     @Test
@@ -131,54 +127,19 @@ public class CommandLineTest {
     }
 
     @Test
-    public void testAddArgumentWithBothQuotes() {
-        final CommandLine cmdl = new CommandLine("test");
-
-        try {
-            cmdl.addArgument("b\"a'r");
-            fail("IllegalArgumentException should be thrown");
-        } catch (final IllegalArgumentException e) {
-            // OK, expected
-        }
-    }
-
-    @Test
-    public void testAddArguments() {
-        final CommandLine cmdl = new CommandLine("test");
-        cmdl.addArguments("foo bar");
-        assertEquals("[test, foo, bar]", cmdl.toString());
-        assertArrayEquals(new String[]{"test", "foo", "bar"}, cmdl.toStrings());
-    }
-
-    @Test
-    public void testAddArgumentsWithQuotes() {
-        final CommandLine cmdl = new CommandLine("test");
-        cmdl.addArguments("'foo' \"bar\"");
-        assertEquals("[test, foo, bar]", cmdl.toString());
-        assertArrayEquals(new String[]{"test", "foo", "bar"}, cmdl.toStrings());
-    }
-
-    @Test
-    public void testAddArgumentsWithQuotesAndSpaces() {
+    public void testAddArgumentWithSpace() {
         final CommandLine cmdl = new CommandLine("test");
-        cmdl.addArguments("'fo o' \"ba r\"");
-        assertEquals("[test, \"fo o\", \"ba r\"]", cmdl.toString());
-        assertArrayEquals(new String[]{"test", "\"fo o\"", "\"ba r\""}, cmdl
-                .toStrings());
+        cmdl.addArgument("foo");
+        cmdl.addArgument("ba r");
+        assertEquals("[test, foo, \"ba r\"]", cmdl.toString());
+        assertArrayEquals(new String[]{"test", "foo", "\"ba r\""}, cmdl.toStrings());
     }
 
     @Test
-    public void testAddArgumentsArray() {
+    public void testAddNullArgument() {
         final CommandLine cmdl = new CommandLine("test");
-        cmdl.addArguments(new String[] {"foo", "bar"});
-        assertEquals("[test, foo, bar]", cmdl.toString());
-        assertArrayEquals(new String[]{"test", "foo", "bar"}, cmdl.toStrings());
-    }
 
-    @Test
-    public void testAddArgumentsArrayNull() {
-        final CommandLine cmdl = new CommandLine("test");
-        cmdl.addArguments((String[]) null);
+        cmdl.addArgument(null);
         assertEquals("[test]", cmdl.toString());
         assertArrayEquals(new String[]{"test"}, cmdl.toStrings());
     }
@@ -203,142 +164,6 @@ public class CommandLineTest {
         assertEquals(userAddCL1.toString(), userAddCL2.toString());
     }
 
-    @Test
-    public void testParseCommandLine() {
-        final CommandLine cmdl = CommandLine.parse("test foo bar");
-        assertEquals("[test, foo, bar]", cmdl.toString());
-        assertArrayEquals(new String[]{"test", "foo", "bar"}, cmdl.toStrings());
-    }
-
-    @Test
-    public void testParseCommandLineWithQuotes() {
-        final CommandLine cmdl = CommandLine.parse("test \"foo\" \'ba r\'");
-        assertEquals("[test, foo, \"ba r\"]", cmdl.toString());
-        assertArrayEquals(new String[]{"test", "foo", "\"ba r\""}, cmdl.toStrings());
-    }
-
-    @Test
-    public void testParseCommandLineWithUnevenQuotes() {
-        try {
-            CommandLine.parse("test \"foo bar");
-            fail("IllegalArgumentException must be thrown due to uneven quotes");
-        } catch (final IllegalArgumentException e) {
-            // Expected
-        }
-    }
-
-    @Test
-    public void testParseCommandLineWithNull() {
-        try {
-            CommandLine.parse(null);
-            fail("IllegalArgumentException must be thrown due to incorrect command line");
-        } catch (final IllegalArgumentException e) {
-            // Expected
-        }
-    }
-
-    @Test
-    public void testParseCommandLineWithOnlyWhitespace() {
-        try {
-            CommandLine.parse("  ");
-            fail("IllegalArgumentException must be thrown due to incorrect command line");
-        } catch (final IllegalArgumentException e) {
-            // Expected
-        }
-    }
-
-    /**
-     * A command line parsing puzzle from Tino Schoellhorn - ImageMagix expects
-     * a "500x>" parameter (including quotes) and it is simply not possible to
-     * do that without adding a space, e.g. "500x> ".
-     */
-    @Test
-    public void testParseComplexCommandLine1() {
-        final HashMap<String, String> substitutionMap =
-            new HashMap<>();
-        substitutionMap.put("in", "source.jpg");
-        substitutionMap.put("out", "target.jpg");
-        final CommandLine cmdl = CommandLine.parse("cmd /C convert ${in} -resize \"\'500x> \'\" ${out}", substitutionMap);
-        assertEquals("[cmd, /C, convert, source.jpg, -resize, \"500x> \", target.jpg]", cmdl.toString());
-    }
-
-    /**
-     * Another  command line parsing puzzle from Kai Hu - as
-     * far as I understand it there is no way to express that
-     * in a one-line command string.
-     */
-    @Test
-    public void testParseComplexCommandLine2() {
-
-        final String commandline = "./script/jrake cruise:publish_installers "
-            + "INSTALLER_VERSION=unstable_2_1 "
-            + "INSTALLER_PATH=\"/var/lib/ cruise-agent/installers\" "
-            + "INSTALLER_DOWNLOAD_SERVER=\'something\' "
-            + "WITHOUT_HELP_DOC=true";
-
-        final CommandLine cmdl = CommandLine.parse(commandline);
-        final String[] args = cmdl.getArguments();
-        assertEquals(args[0], "cruise:publish_installers");
-        assertEquals(args[1], "INSTALLER_VERSION=unstable_2_1");
-        // assertEquals(args[2], "INSTALLER_PATH=\"/var/lib/ cruise-agent/installers\"");
-        // assertEquals(args[3], "INSTALLER_DOWNLOAD_SERVER='something'");
-        assertEquals(args[4], "WITHOUT_HELP_DOC=true");
-    }
-
-    /**
-     * Test the following command line
-     *
-     * cmd.exe /C c:\was51\Web Sphere\AppServer\bin\versionInfo.bat
-     */
-    @Test
-    public void testParseRealLifeCommandLine_1() {
-
-        final String commandline = "cmd.exe /C \"c:\\was51\\Web Sphere\\AppServer\\bin\\versionInfo.bat\"";
-
-        final CommandLine cmdl = CommandLine.parse(commandline);
-        final String[] args = cmdl.getArguments();
-        assertEquals("/C", args[0]);
-        assertEquals("\"c:\\was51\\Web Sphere\\AppServer\\bin\\versionInfo.bat\"", args[1]);
-    }
-
-   /**
-    * Create a command line with pre-quoted strings to test SANDBOX-192,
-    * e.g. "runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""
-    */
-   @Test
-    public void testComplexAddArgument() {
-        final CommandLine cmdl = new CommandLine("runMemorySud.cmd");
-        cmdl.addArgument("10", false);
-        cmdl.addArgument("30", false);
-        cmdl.addArgument("-XX:+UseParallelGC", false);
-        cmdl.addArgument("\"-XX:ParallelGCThreads=2\"", false);
-        assertArrayEquals(new String[]{"runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""}, cmdl.toStrings());
-    }
-
-    /**
-     * Create a command line with pre-quoted strings to test SANDBOX-192,
-     * e.g. "runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""
-     */
-    @Test
-     public void testComplexAddArguments1() {
-         final CommandLine cmdl = new CommandLine("runMemorySud.cmd");
-         cmdl.addArguments(new String[] {"10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""}, false);
-         assertArrayEquals(new String[]{"runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""}, cmdl.toStrings());
-     }
-
-    /**
-     * Create a command line with pre-quoted strings to test SANDBOX-192,
-     * e.g. "runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""
-     * Please not that we re forced to add additional single quotes to get the test working -
-     * don't know if this is a bug or a feature.
-     */
-    @Test
-     public void testComplexAddArguments2() {
-         final CommandLine cmdl = new CommandLine("runMemorySud.cmd");
-         cmdl.addArguments("10 30 -XX:+UseParallelGC '\"-XX:ParallelGCThreads=2\"'", false);
-         assertArrayEquals(new String[]{"runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""}, cmdl.toStrings());
-     }
-
     /**
      * Test expanding the command line based on a user-supplied map.
      */
@@ -455,6 +280,198 @@ public class CommandLineTest {
         assertEquals("C:\\Document And Settings\\documents\\432432.pdf", result[3]);
 
     }
+
+    /**
+        * Create a command line with pre-quoted strings to test SANDBOX-192,
+        * e.g. "runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""
+        */
+       @Test
+        public void testComplexAddArgument() {
+            final CommandLine cmdl = new CommandLine("runMemorySud.cmd");
+            cmdl.addArgument("10", false);
+            cmdl.addArgument("30", false);
+            cmdl.addArgument("-XX:+UseParallelGC", false);
+            cmdl.addArgument("\"-XX:ParallelGCThreads=2\"", false);
+            assertArrayEquals(new String[]{"runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""}, cmdl.toStrings());
+        }
+
+    /**
+     * Create a command line with pre-quoted strings to test SANDBOX-192,
+     * e.g. "runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""
+     */
+    @Test
+     public void testComplexAddArguments1() {
+         final CommandLine cmdl = new CommandLine("runMemorySud.cmd");
+         cmdl.addArguments(new String[] {"10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""}, false);
+         assertArrayEquals(new String[]{"runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""}, cmdl.toStrings());
+     }
+
+    /**
+     * Create a command line with pre-quoted strings to test SANDBOX-192,
+     * e.g. "runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""
+     * Please not that we re forced to add additional single quotes to get the test working -
+     * don't know if this is a bug or a feature.
+     */
+    @Test
+     public void testComplexAddArguments2() {
+         final CommandLine cmdl = new CommandLine("runMemorySud.cmd");
+         cmdl.addArguments("10 30 -XX:+UseParallelGC '\"-XX:ParallelGCThreads=2\"'", false);
+         assertArrayEquals(new String[]{"runMemorySud.cmd", "10", "30", "-XX:+UseParallelGC", "\"-XX:ParallelGCThreads=2\""}, cmdl.toStrings());
+     }
+
+    @Test
+    public void testCopyConstructor()
+    {
+        final Map<String, String> map = new HashMap<>();
+        map.put("bar", "bar");
+        final CommandLine other = new CommandLine("test");
+        other.addArgument("foo");
+        other.setSubstitutionMap(map);
+
+        final CommandLine cmdl = new CommandLine(other);
+        assertEquals(other.getExecutable(), cmdl.getExecutable());
+        assertArrayEquals(other.getArguments(), cmdl.getArguments());
+        assertEquals(other.isFile(), cmdl.isFile());
+        assertEquals(other.getSubstitutionMap(), cmdl.getSubstitutionMap());
+
+    }
+
+    @Test
+    public void testExecutable() {
+        final CommandLine cmdl = new CommandLine("test");
+        assertEquals("[test]", cmdl.toString());
+        assertArrayEquals(new String[]{"test"}, cmdl.toStrings());
+        assertEquals("test", cmdl.getExecutable());
+        assertTrue(cmdl.getArguments().length == 0);
+    }
+
+    @Test
+    public void testExecutableWhitespaceString() {
+        try {
+            new CommandLine("   ");
+            fail("Must throw IllegalArgumentException");
+        } catch (final IllegalArgumentException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void testExecutableZeroLengthString() {
+        try {
+            new CommandLine("");
+            fail("Must throw IllegalArgumentException");
+        } catch (final IllegalArgumentException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void testNullExecutable() {
+        try {
+            new CommandLine((String)null);
+            fail("Must throw IllegalArgumentException");
+        } catch (final IllegalArgumentException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void testParseCommandLine() {
+        final CommandLine cmdl = CommandLine.parse("test foo bar");
+        assertEquals("[test, foo, bar]", cmdl.toString());
+        assertArrayEquals(new String[]{"test", "foo", "bar"}, cmdl.toStrings());
+    }
+
+   @Test
+public void testParseCommandLineWithNull() {
+    try {
+        CommandLine.parse(null);
+        fail("IllegalArgumentException must be thrown due to incorrect command line");
+    } catch (final IllegalArgumentException e) {
+        // Expected
+    }
+}
+
+    @Test
+    public void testParseCommandLineWithOnlyWhitespace() {
+        try {
+            CommandLine.parse("  ");
+            fail("IllegalArgumentException must be thrown due to incorrect command line");
+        } catch (final IllegalArgumentException e) {
+            // Expected
+        }
+    }
+
+    @Test
+    public void testParseCommandLineWithQuotes() {
+        final CommandLine cmdl = CommandLine.parse("test \"foo\" \'ba r\'");
+        assertEquals("[test, foo, \"ba r\"]", cmdl.toString());
+        assertArrayEquals(new String[]{"test", "foo", "\"ba r\""}, cmdl.toStrings());
+    }
+
+    @Test
+    public void testParseCommandLineWithUnevenQuotes() {
+        try {
+            CommandLine.parse("test \"foo bar");
+            fail("IllegalArgumentException must be thrown due to uneven quotes");
+        } catch (final IllegalArgumentException e) {
+            // Expected
+        }
+    }
+
+    /**
+     * A command line parsing puzzle from Tino Schoellhorn - ImageMagix expects
+     * a "500x>" parameter (including quotes) and it is simply not possible to
+     * do that without adding a space, e.g. "500x> ".
+     */
+    @Test
+    public void testParseComplexCommandLine1() {
+        final HashMap<String, String> substitutionMap =
+            new HashMap<>();
+        substitutionMap.put("in", "source.jpg");
+        substitutionMap.put("out", "target.jpg");
+        final CommandLine cmdl = CommandLine.parse("cmd /C convert ${in} -resize \"\'500x> \'\" ${out}", substitutionMap);
+        assertEquals("[cmd, /C, convert, source.jpg, -resize, \"500x> \", target.jpg]", cmdl.toString());
+    }
+
+    /**
+     * Another  command line parsing puzzle from Kai Hu - as
+     * far as I understand it there is no way to express that
+     * in a one-line command string.
+     */
+    @Test
+    public void testParseComplexCommandLine2() {
+
+        final String commandline = "./script/jrake cruise:publish_installers "
+            + "INSTALLER_VERSION=unstable_2_1 "
+            + "INSTALLER_PATH=\"/var/lib/ cruise-agent/installers\" "
+            + "INSTALLER_DOWNLOAD_SERVER=\'something\' "
+            + "WITHOUT_HELP_DOC=true";
+
+        final CommandLine cmdl = CommandLine.parse(commandline);
+        final String[] args = cmdl.getArguments();
+        assertEquals(args[0], "cruise:publish_installers");
+        assertEquals(args[1], "INSTALLER_VERSION=unstable_2_1");
+        // assertEquals(args[2], "INSTALLER_PATH=\"/var/lib/ cruise-agent/installers\"");
+        // assertEquals(args[3], "INSTALLER_DOWNLOAD_SERVER='something'");
+        assertEquals(args[4], "WITHOUT_HELP_DOC=true");
+    }
+    /**
+     * Test the following command line
+     *
+     * cmd.exe /C c:\was51\Web Sphere\AppServer\bin\versionInfo.bat
+     */
+    @Test
+    public void testParseRealLifeCommandLine_1() {
+
+        final String commandline = "cmd.exe /C \"c:\\was51\\Web Sphere\\AppServer\\bin\\versionInfo.bat\"";
+
+        final CommandLine cmdl = CommandLine.parse(commandline);
+        final String[] args = cmdl.getArguments();
+        assertEquals("/C", args[0]);
+        assertEquals("\"c:\\was51\\Web Sphere\\AppServer\\bin\\versionInfo.bat\"", args[1]);
+    }
+
     /**
      * Test the toString() method.
      *
@@ -501,21 +518,4 @@ public class CommandLineTest {
                 !cmd1.toString().equals(cmd2.toString()));
     }
 
-    @Test
-    public void testCopyConstructor()
-    {
-        final Map<String, String> map = new HashMap<>();
-        map.put("bar", "bar");
-        final CommandLine other = new CommandLine("test");
-        other.addArgument("foo");
-        other.setSubstitutionMap(map);
-
-        final CommandLine cmdl = new CommandLine(other);
-        assertEquals(other.getExecutable(), cmdl.getExecutable());
-        assertArrayEquals(other.getArguments(), cmdl.getArguments());
-        assertEquals(other.isFile(), cmdl.isFile());
-        assertEquals(other.getSubstitutionMap(), cmdl.getSubstitutionMap());
-
-    }
-
 }
diff --git a/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java b/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
index 3fc955d0..96aaac31 100644
--- a/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
+++ b/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java
@@ -52,36 +52,103 @@ public class DefaultExecutorTest {
     /** Maximum time to wait (15s) */
     private static final int WAITFOR_TIMEOUT = 15000;
 
+    // Get suitable exit codes for the OS
+    private static int SUCCESS_STATUS; // test script successful exit code
+    private static int ERROR_STATUS;   // test script error exit code
+    @BeforeClass
+    public static void classSetUp() {
+
+        final int[] statuses = TestUtil.getTestScriptCodesForOS();
+        SUCCESS_STATUS=statuses[0];
+        ERROR_STATUS=statuses[1];
+
+        // turn on debug mode and throw an exception for each encountered problem
+        System.setProperty("org.apache.commons.exec.lenient", "false");
+        System.setProperty("org.apache.commons.exec.debug", "true");
+    }
     private final Executor exec = new DefaultExecutor();
+
     private final File testDir = new File("src/test/scripts");
     private final File foreverOutputFile = new File("./target/forever.txt");
     private ByteArrayOutputStream baos;
-
     private final File testScript = TestUtil.resolveScriptForOS(testDir + "/test");
     private final File errorTestScript = TestUtil.resolveScriptForOS(testDir + "/error");
     private final File foreverTestScript = TestUtil.resolveScriptForOS(testDir + "/forever");
-    private final File nonExistingTestScript = TestUtil.resolveScriptForOS(testDir + "/grmpffffff");
+private final File nonExistingTestScript = TestUtil.resolveScriptForOS(testDir + "/grmpffffff");
     private final File redirectScript = TestUtil.resolveScriptForOS(testDir + "/redirect");
+
     private final File printArgsScript = TestUtil.resolveScriptForOS(testDir + "/printargs");
-//    private final File acroRd32Script = TestUtil.resolveScriptForOS(testDir + "/acrord32");
+    //    private final File acroRd32Script = TestUtil.resolveScriptForOS(testDir + "/acrord32");
     private final File stdinSript = TestUtil.resolveScriptForOS(testDir + "/stdin");
+
     private final File environmentSript = TestUtil.resolveScriptForOS(testDir + "/environment");
 //    private final File wrapperScript = TestUtil.resolveScriptForOS(testDir + "/wrapper");
 
-    // Get suitable exit codes for the OS
-    private static int SUCCESS_STATUS; // test script successful exit code
-    private static int ERROR_STATUS;   // test script error exit code
+    /**
+     * Start any processes in a loop to make sure that we do
+     * not leave any handles/resources open.
+     *
+     * @throws Exception the test failed
+     */
+    @Test
+    @Ignore
+    public void _testExecuteStability() throws Exception {
 
-    @BeforeClass
-    public static void classSetUp() {
+        // make a plain-vanilla test
+        for (int i = 0; i < 100; i++) {
+            final Map<String, String> env = new HashMap<>();
+            env.put("TEST_ENV_VAR", Integer.toString(i));
+            final CommandLine cl = new CommandLine(testScript);
+            final int exitValue = exec.execute(cl, env);
+            assertFalse(exec.isFailure(exitValue));
+            assertEquals("FOO." + i + ".", baos.toString().trim());
+            baos.reset();
+        }
 
-        final int[] statuses = TestUtil.getTestScriptCodesForOS();
-        SUCCESS_STATUS=statuses[0];
-        ERROR_STATUS=statuses[1];
+        // now be nasty and use the watchdog to kill out sub-processes
+        for (int i = 0; i < 100; i++) {
+            final Map<String, String> env = new HashMap<>();
+            env.put("TEST_ENV_VAR", Integer.toString(i));
+            final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
+            final CommandLine cl = new CommandLine(foreverTestScript);
+            final ExecuteWatchdog watchdog = new ExecuteWatchdog(500);
+            exec.setWatchdog(watchdog);
+            exec.execute(cl, env, resultHandler);
+            resultHandler.waitFor(WAITFOR_TIMEOUT);
+            assertTrue("ResultHandler received a result", resultHandler.hasResult());
+            assertNotNull(resultHandler.getException());
+            baos.reset();
+        }
+    }
 
-        // turn on debug mode and throw an exception for each encountered problem
-        System.setProperty("org.apache.commons.exec.lenient", "false");
-        System.setProperty("org.apache.commons.exec.debug", "true");
+    private int getOccurrences(final String data, final char c) {
+
+        int result = 0;
+
+        for (int i=0; i<data.length(); i++) {
+            if (data.charAt(i) == c) {
+                result++;
+            }
+        }
+
+        return result;
+    }
+
+    // ======================================================================
+    // Start of regression tests
+    // ======================================================================
+
+    private String readFile(final File file) throws Exception {
+
+        String text;
+        final StringBuilder contents = new StringBuilder();
+        final BufferedReader reader = new BufferedReader(new FileReader(file));
+
+        while ((text = reader.readLine()) != null) {
+            contents.append(text).append(System.getProperty("line.separator"));
+        }
+        reader.close();
+        return contents.toString();
     }
 
     @Before
@@ -104,83 +171,61 @@ public class DefaultExecutorTest {
         foreverOutputFile.delete();
     }
 
-    // ======================================================================
-    // Start of regression tests
-    // ======================================================================
+    @Test
+    public void testAddEnvironmentVariableEmbeddedQuote() throws Exception {
+        final Map<String, String> myEnvVars = new HashMap<>(EnvironmentUtils.getProcEnvironment());
+        final String name = "NEW_VAR";
+        final String value = "NEW_\"_VAL";
+        myEnvVars.put(name, value);
+        exec.execute(new CommandLine(environmentSript), myEnvVars);
+        final String environment = baos.toString().trim();
+        assertTrue("Expecting " + name + " in " + environment, environment.indexOf(name) >= 0);
+        assertTrue("Expecting " + value + " in " + environment, environment.indexOf(value) >= 0);
+    }
 
     /**
-     * The simplest possible test - start a script and
-     * check that the output was pumped into our
-     * {@code ByteArrayOutputStream}.
+     * Call a script to dump the environment variables of the subprocess
+     * after adding a custom environment variable.
      *
      * @throws Exception the test failed
      */
     @Test
-    public void testExecute() throws Exception {
-        final CommandLine cl = new CommandLine(testScript);
-        final int exitValue = exec.execute(cl);
-        assertEquals("FOO..", baos.toString().trim());
-        assertFalse(exec.isFailure(exitValue));
-        assertEquals(new File("."), exec.getWorkingDirectory());
-    }
-
-    @Test
-    public void testExecuteWithWorkingDirectory() throws Exception {
-        final File workingDir = new File("./target");
-        final CommandLine cl = new CommandLine(testScript);
-        exec.setWorkingDirectory(workingDir);
-        final int exitValue = exec.execute(cl);
-        assertEquals("FOO..", baos.toString().trim());
-        assertFalse(exec.isFailure(exitValue));
-        assertEquals(exec.getWorkingDirectory(), workingDir);
-    }
-
-    @Test
-    public void testExecuteWithInvalidWorkingDirectory() throws Exception {
-        final File workingDir = new File("/foo/bar");
-        final CommandLine cl = new CommandLine(testScript);
-        exec.setWorkingDirectory(workingDir);
-
-        assertThrows(IOException.class, () -> exec.execute(cl));
-    }
-
-    @Test
-    public void testExecuteWithError() throws Exception {
-        final CommandLine cl = new CommandLine(errorTestScript);
-
-        try{
-            exec.execute(cl);
-            fail("Must throw ExecuteException");
-        } catch (final ExecuteException e) {
-            assertTrue(exec.isFailure(e.getExitValue()));
-        }
+    public void testAddEnvironmentVariables() throws Exception {
+        final Map<String, String> myEnvVars = new HashMap<>(EnvironmentUtils.getProcEnvironment());
+        myEnvVars.put("NEW_VAR", "NEW_VAL");
+        exec.execute(new CommandLine(environmentSript), myEnvVars);
+        final String environment = baos.toString().trim();
+        assertTrue("Expecting NEW_VAR in " + environment, environment.indexOf("NEW_VAR") >= 0);
+        assertTrue("Expecting NEW_VAL in " + environment, environment.indexOf("NEW_VAL") >= 0);
     }
 
+    /**
+     * Call a script to dump the environment variables of the subprocess.
+     *
+     * @throws Exception the test failed
+     */
     @Test
-    public void testExecuteWithArg() throws Exception {
-        final CommandLine cl = new CommandLine(testScript);
-        cl.addArgument("BAR");
-        final int exitValue = exec.execute(cl);
-
-        assertEquals("FOO..BAR", baos.toString().trim());
-        assertFalse(exec.isFailure(exitValue));
+    public void testEnvironmentVariables() throws Exception {
+        exec.execute(new CommandLine(environmentSript));
+        final String environment = baos.toString().trim();
+        assertFalse("Found no environment variables", environment.isEmpty());
+        assertFalse(environment.indexOf("NEW_VAR") >= 0);
     }
 
     /**
-     * Execute the test script and pass a environment containing
-     * 'TEST_ENV_VAR'.
+     * The simplest possible test - start a script and
+     * check that the output was pumped into our
+     * {@code ByteArrayOutputStream}.
+     *
+     * @throws Exception the test failed
      */
     @Test
-    public void testExecuteWithSingleEnvironmentVariable() throws Exception {
-        final Map<String, String> env = new HashMap<>();
-        env.put("TEST_ENV_VAR", "XYZ");
-
+    public void testExecute() throws Exception {
         final CommandLine cl = new CommandLine(testScript);
-
-        final int exitValue = exec.execute(cl, env);
-
-        assertEquals("FOO.XYZ.", baos.toString().trim());
+        final int exitValue = exec.execute(cl);
+        assertEquals("FOO..", baos.toString().trim());
         assertFalse(exec.isFailure(exitValue));
+        assertEquals(new File("."), exec.getWorkingDirectory());
     }
 
     /**
@@ -201,6 +246,53 @@ public class DefaultExecutorTest {
         assertEquals("FOO..", baos.toString().trim());
     }
 
+    /**
+     * Try to start an non-existing application where the exception is caught/processed
+     * by the result handler.
+     */
+    @Test
+    public void testExecuteAsyncNonExistingApplication() throws Exception {
+        final CommandLine cl = new CommandLine(nonExistingTestScript);
+        final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
+        final DefaultExecutor executor = new DefaultExecutor();
+
+        executor.execute(cl, resultHandler);
+        resultHandler.waitFor();
+
+        assertTrue(executor.isFailure(resultHandler.getExitValue()));
+        assertNotNull(resultHandler.getException());
+    }
+
+    /**
+     * Try to start an non-existing application where the exception is caught/processed
+     * by the result handler. The watchdog in notified to avoid waiting for the
+     * process infinitely.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/EXEC-71">EXEC-71</a>
+     */
+    @Test
+    public void testExecuteAsyncNonExistingApplicationWithWatchdog() throws Exception {
+        final CommandLine cl = new CommandLine(nonExistingTestScript);
+        final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler() {
+            @Override
+            public void onProcessFailed(final ExecuteException e) {
+                System.out.println("Process did not stop gracefully, had exception '" + e.getMessage() + "' while executing process");
+                super.onProcessFailed(e);
+            }
+        };
+        final DefaultExecutor executor = new DefaultExecutor();
+        executor.setWatchdog(new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT));
+
+        executor.execute(cl, resultHandler);
+        resultHandler.waitFor();
+
+        assertTrue(executor.isFailure(resultHandler.getExitValue()));
+        assertNotNull(resultHandler.getException());
+        assertFalse(executor.getWatchdog().isWatching());
+        assertFalse(executor.getWatchdog().killedProcess());
+        executor.getWatchdog().destroyProcess();
+    }
+
     /**
      * Start a asynchronous process which returns an error
      * exit value.
@@ -219,6 +311,47 @@ public class DefaultExecutorTest {
         assertEquals("FOO..", baos.toString().trim());
     }
 
+    /**
+     * Test the proper handling of ProcessDestroyer for an asynchronous process.
+     * Since we do not terminate the process it will be terminated in the
+     * ShutdownHookProcessDestroyer implementation.
+     *
+     * @throws Exception the test failed
+     */
+    @Test
+    public void testExecuteAsyncWithProcessDestroyer() throws Exception {
+
+      final CommandLine cl = new CommandLine(foreverTestScript);
+      final DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
+      final ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
+      final ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
+
+      assertNull(exec.getProcessDestroyer());
+      assertTrue(processDestroyer.isEmpty());
+      assertFalse(processDestroyer.isAddedAsShutdownHook());
+
+      exec.setWatchdog(watchdog);
+      exec.setProcessDestroyer(processDestroyer);
+      exec.execute(cl, handler);
+
+      // wait for script to start
+      Thread.sleep(2000);
+
+      // our process destroyer should be initialized now
+      assertNotNull("Process destroyer should exist", exec.getProcessDestroyer());
+      assertEquals("Process destroyer size should be 1", 1, processDestroyer.size());
+      assertTrue("Process destroyer should exist as shutdown hook", processDestroyer.isAddedAsShutdownHook());
+
+      // terminate it and the process destroyer is detached
+      watchdog.destroyProcess();
+      assertTrue(watchdog.killedProcess());
+      handler.waitFor(WAITFOR_TIMEOUT);
+      assertTrue("ResultHandler received a result", handler.hasResult());
+      assertNotNull(handler.getException());
+      assertEquals("Processor Destroyer size should be 0", 0, processDestroyer.size());
+      assertFalse("Process destroyer should not exist as shutdown hook", processDestroyer.isAddedAsShutdownHook());
+    }
+
     /**
      * Start a asynchronous process and terminate it manually before the
      * watchdog timeout occurs.
@@ -272,6 +405,62 @@ public class DefaultExecutorTest {
         assertNotNull("ResultHandler received an exception as result", handler.getException());
     }
 
+    /**
+     * Try to start an non-existing application which should result
+     * in an exception.
+     */
+    @Test
+    public void testExecuteNonExistingApplication() throws Exception {
+        final CommandLine cl = new CommandLine(nonExistingTestScript);
+        final DefaultExecutor executor = new DefaultExecutor();
+
+        assertThrows(IOException.class, () -> executor.execute(cl));
+    }
+
+    /**
+     * Try to start an non-existing application which should result
+     * in an exception.
+     */
+    @Test
+    public void testExecuteNonExistingApplicationWithWatchDog() throws Exception {
+        final CommandLine cl = new CommandLine(nonExistingTestScript);
+        final DefaultExecutor executor = new DefaultExecutor();
+        executor.setWatchdog(new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT));
+
+        assertThrows(IOException.class, () -> executor.execute(cl));
+    }
+
+    /**
+     * Start a script looping forever (asynchronously) and check if the
+     * ExecuteWatchdog kicks in killing the run away process. To make killing
+     * a process more testable the "forever" scripts write each second a '.'
+     * into "./target/forever.txt" (a marker file). After a test run
+     * we should have a few dots in there.
+     *
+     * @throws Exception the test failed
+     */
+    @Test
+    public void testExecuteWatchdogAsync() throws Exception {
+
+        final long timeout = 10000;
+
+        final CommandLine cl = new CommandLine(foreverTestScript);
+        final DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
+        final DefaultExecutor executor = new DefaultExecutor();
+        executor.setWorkingDirectory(new File("."));
+        executor.setWatchdog(new ExecuteWatchdog(timeout));
+
+        executor.execute(cl, handler);
+        handler.waitFor(WAITFOR_TIMEOUT);
+
+        assertTrue("Killed process should be true", executor.getWatchdog().killedProcess() );
+        assertTrue("ResultHandler received a result", handler.hasResult());
+        assertNotNull("ResultHandler received an exception as result", handler.getException());
+
+        final int nrOfInvocations = getOccurrences(readFile(this.foreverOutputFile), '.');
+        assertTrue("Killing the process did not work : " + nrOfInvocations, nrOfInvocations > 5 && nrOfInvocations <= 11);
+    }
+
     /**
      * Start a script looping forever (synchronously) and check if the ExecuteWatchdog
      * kicks in killing the run away process. To make killing a process
@@ -317,37 +506,6 @@ public class DefaultExecutorTest {
         fail("Process did not create ExecuteException when killed");
     }
 
-    /**
-     * Start a script looping forever (asynchronously) and check if the
-     * ExecuteWatchdog kicks in killing the run away process. To make killing
-     * a process more testable the "forever" scripts write each second a '.'
-     * into "./target/forever.txt" (a marker file). After a test run
-     * we should have a few dots in there.
-     *
-     * @throws Exception the test failed
-     */
-    @Test
-    public void testExecuteWatchdogAsync() throws Exception {
-
-        final long timeout = 10000;
-
-        final CommandLine cl = new CommandLine(foreverTestScript);
-        final DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
-        final DefaultExecutor executor = new DefaultExecutor();
-        executor.setWorkingDirectory(new File("."));
-        executor.setWatchdog(new ExecuteWatchdog(timeout));
-
-        executor.execute(cl, handler);
-        handler.waitFor(WAITFOR_TIMEOUT);
-
-        assertTrue("Killed process should be true", executor.getWatchdog().killedProcess() );
-        assertTrue("ResultHandler received a result", handler.hasResult());
-        assertNotNull("ResultHandler received an exception as result", handler.getException());
-
-        final int nrOfInvocations = getOccurrences(readFile(this.foreverOutputFile), '.');
-        assertTrue("Killing the process did not work : " + nrOfInvocations, nrOfInvocations > 5 && nrOfInvocations <= 11);
-    }
-
     /**
      * [EXEC-68] Synchronously starts a short script with a Watchdog attached with an extremely large timeout. Checks
      * to see if the script terminated naturally or if it was killed by the Watchdog. Fail if killed by Watchdog.
@@ -374,77 +532,31 @@ public class DefaultExecutorTest {
         }
     }
 
-    /**
-     * Try to start an non-existing application which should result
-     * in an exception.
-     */
-    @Test
-    public void testExecuteNonExistingApplication() throws Exception {
-        final CommandLine cl = new CommandLine(nonExistingTestScript);
-        final DefaultExecutor executor = new DefaultExecutor();
-
-        assertThrows(IOException.class, () -> executor.execute(cl));
-    }
-
-    /**
-     * Try to start an non-existing application which should result
-     * in an exception.
-     */
     @Test
-    public void testExecuteNonExistingApplicationWithWatchDog() throws Exception {
-        final CommandLine cl = new CommandLine(nonExistingTestScript);
-        final DefaultExecutor executor = new DefaultExecutor();
-        executor.setWatchdog(new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT));
-
-        assertThrows(IOException.class, () -> executor.execute(cl));
-    }
-
-    /**
-     * Try to start an non-existing application where the exception is caught/processed
-     * by the result handler.
-     */
-    @Test
-    public void testExecuteAsyncNonExistingApplication() throws Exception {
-        final CommandLine cl = new CommandLine(nonExistingTestScript);
-        final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
-        final DefaultExecutor executor = new DefaultExecutor();
-
-        executor.execute(cl, resultHandler);
-        resultHandler.waitFor();
+    public void testExecuteWithArg() throws Exception {
+        final CommandLine cl = new CommandLine(testScript);
+        cl.addArgument("BAR");
+        final int exitValue = exec.execute(cl);
 
-        assertTrue(executor.isFailure(resultHandler.getExitValue()));
-        assertNotNull(resultHandler.getException());
+        assertEquals("FOO..BAR", baos.toString().trim());
+        assertFalse(exec.isFailure(exitValue));
     }
 
     /**
-     * Try to start an non-existing application where the exception is caught/processed
-     * by the result handler. The watchdog in notified to avoid waiting for the
-     * process infinitely.
+     * A generic test case to print the command line arguments to 'printargs' script to solve
+     * even more command line puzzles.
      *
-     * @see <a href="https://issues.apache.org/jira/browse/EXEC-71">EXEC-71</a>
+     * @throws Exception the test failed
      */
     @Test
-    public void testExecuteAsyncNonExistingApplicationWithWatchdog() throws Exception {
-        final CommandLine cl = new CommandLine(nonExistingTestScript);
-        final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler() {
-            @Override
-            public void onProcessFailed(final ExecuteException e) {
-                System.out.println("Process did not stop gracefully, had exception '" + e.getMessage() + "' while executing process");
-                super.onProcessFailed(e);
-            }
-        };
+    public void testExecuteWithComplexArguments() throws Exception {
+        final CommandLine cl = new CommandLine(printArgsScript);
+        cl.addArgument("gdal_translate");
+        cl.addArgument("HDF5:\"/home/kk/grass/data/4404.he5\"://HDFEOS/GRIDS/OMI_Column_Amount_O3/Data_Fields/ColumnAmountO3/home/kk/4.tif", false);
         final DefaultExecutor executor = new DefaultExecutor();
-        executor.setWatchdog(new ExecuteWatchdog(ExecuteWatchdog.INFINITE_TIMEOUT));
-
-        executor.execute(cl, resultHandler);
-        resultHandler.waitFor();
-
-        assertTrue(executor.isFailure(resultHandler.getExitValue()));
-        assertNotNull(resultHandler.getException());
-        assertFalse(executor.getWatchdog().isWatching());
-        assertFalse(executor.getWatchdog().killedProcess());
-        executor.getWatchdog().destroyProcess();
-    }
+        final int exitValue = executor.execute(cl);
+        assertFalse(exec.isFailure(exitValue));
+     }
 
     /**
      * Invoke the error script but define that the ERROR_STATUS is a good
@@ -477,6 +589,57 @@ public class DefaultExecutorTest {
         }
     }
 
+     @Test
+    public void testExecuteWithError() throws Exception {
+        final CommandLine cl = new CommandLine(errorTestScript);
+
+        try{
+            exec.execute(cl);
+            fail("Must throw ExecuteException");
+        } catch (final ExecuteException e) {
+            assertTrue(exec.isFailure(e.getExitValue()));
+        }
+    }
+
+    /**
+     * Invoke the test using some fancy arguments.
+     *
+     * @throws Exception the test failed
+     */
+    @Test
+    public void testExecuteWithFancyArg() throws Exception {
+        final CommandLine cl = new CommandLine(testScript);
+        cl.addArgument("test $;`(0)[1]{2}");
+        final int exitValue = exec.execute(cl);
+        assertTrue(baos.toString().trim().indexOf("test $;`(0)[1]{2}") > 0);
+        assertFalse(exec.isFailure(exitValue));
+    }
+
+     @Test
+    public void testExecuteWithInvalidWorkingDirectory() throws Exception {
+        final File workingDir = new File("/foo/bar");
+        final CommandLine cl = new CommandLine(testScript);
+        exec.setWorkingDirectory(workingDir);
+
+        assertThrows(IOException.class, () -> exec.execute(cl));
+    }
+
+    /**
+     * Start a process and connect it to no stream.
+     *
+     * @throws Exception
+     *             the test failed
+     */
+    @Test
+    public void testExecuteWithNullOutErr() throws Exception {
+        final CommandLine cl = new CommandLine(testScript);
+        final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(null, null);
+        final DefaultExecutor executor = new DefaultExecutor();
+        executor.setStreamHandler(pumpStreamHandler);
+        final int exitValue = executor.execute(cl);
+        assertFalse(exec.isFailure(exitValue));
+    }
+
     /**
      * Test the proper handling of ProcessDestroyer for an synchronous process.
      *
@@ -500,61 +663,6 @@ public class DefaultExecutorTest {
       assertFalse(processDestroyer.isAddedAsShutdownHook());
     }
 
-    /**
-     * Test the proper handling of ProcessDestroyer for an asynchronous process.
-     * Since we do not terminate the process it will be terminated in the
-     * ShutdownHookProcessDestroyer implementation.
-     *
-     * @throws Exception the test failed
-     */
-    @Test
-    public void testExecuteAsyncWithProcessDestroyer() throws Exception {
-
-      final CommandLine cl = new CommandLine(foreverTestScript);
-      final DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
-      final ShutdownHookProcessDestroyer processDestroyer = new ShutdownHookProcessDestroyer();
-      final ExecuteWatchdog watchdog = new ExecuteWatchdog(Integer.MAX_VALUE);
-
-      assertNull(exec.getProcessDestroyer());
-      assertTrue(processDestroyer.isEmpty());
-      assertFalse(processDestroyer.isAddedAsShutdownHook());
-
-      exec.setWatchdog(watchdog);
-      exec.setProcessDestroyer(processDestroyer);
-      exec.execute(cl, handler);
-
-      // wait for script to start
-      Thread.sleep(2000);
-
-      // our process destroyer should be initialized now
-      assertNotNull("Process destroyer should exist", exec.getProcessDestroyer());
-      assertEquals("Process destroyer size should be 1", 1, processDestroyer.size());
-      assertTrue("Process destroyer should exist as shutdown hook", processDestroyer.isAddedAsShutdownHook());
-
-      // terminate it and the process destroyer is detached
-      watchdog.destroyProcess();
-      assertTrue(watchdog.killedProcess());
-      handler.waitFor(WAITFOR_TIMEOUT);
-      assertTrue("ResultHandler received a result", handler.hasResult());
-      assertNotNull(handler.getException());
-      assertEquals("Processor Destroyer size should be 0", 0, processDestroyer.size());
-      assertFalse("Process destroyer should not exist as shutdown hook", processDestroyer.isAddedAsShutdownHook());
-    }
-
-    /**
-     * Invoke the test using some fancy arguments.
-     *
-     * @throws Exception the test failed
-     */
-    @Test
-    public void testExecuteWithFancyArg() throws Exception {
-        final CommandLine cl = new CommandLine(testScript);
-        cl.addArgument("test $;`(0)[1]{2}");
-        final int exitValue = exec.execute(cl);
-        assertTrue(baos.toString().trim().indexOf("test $;`(0)[1]{2}") > 0);
-        assertFalse(exec.isFailure(exitValue));
-    }
-
     /**
      * Start a process with redirected streams - stdin of the newly
      * created process is connected to a FileInputStream whereas
@@ -588,38 +696,7 @@ public class DefaultExecutorTest {
         }
     }
 
-     /**
-      * Start a process and connect stdout and stderr.
-      *
-      * @throws Exception the test failed
-      */
-     @Test
-    public void testExecuteWithStdOutErr() throws Exception {
-        final CommandLine cl = new CommandLine(testScript);
-        final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(System.out, System.err);
-        final DefaultExecutor executor = new DefaultExecutor();
-        executor.setStreamHandler(pumpStreamHandler);
-        final int exitValue = executor.execute(cl);
-        assertFalse(exec.isFailure(exitValue));
-    }
-
     /**
-     * Start a process and connect it to no stream.
-     *
-     * @throws Exception
-     *             the test failed
-     */
-    @Test
-    public void testExecuteWithNullOutErr() throws Exception {
-        final CommandLine cl = new CommandLine(testScript);
-        final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(null, null);
-        final DefaultExecutor executor = new DefaultExecutor();
-        executor.setStreamHandler(pumpStreamHandler);
-        final int exitValue = executor.execute(cl);
-        assertFalse(exec.isFailure(exitValue));
-    }
-
-     /**
       * Start a process and connect out and err to a file.
       *
       * @throws Exception the test failed
@@ -641,20 +718,55 @@ public class DefaultExecutorTest {
      }
 
     /**
-     * A generic test case to print the command line arguments to 'printargs' script to solve
-     * even more command line puzzles.
-     *
-     * @throws Exception the test failed
+     * Execute the test script and pass a environment containing
+     * 'TEST_ENV_VAR'.
      */
     @Test
-    public void testExecuteWithComplexArguments() throws Exception {
-        final CommandLine cl = new CommandLine(printArgsScript);
-        cl.addArgument("gdal_translate");
-        cl.addArgument("HDF5:\"/home/kk/grass/data/4404.he5\"://HDFEOS/GRIDS/OMI_Column_Amount_O3/Data_Fields/ColumnAmountO3/home/kk/4.tif", false);
+    public void testExecuteWithSingleEnvironmentVariable() throws Exception {
+        final Map<String, String> env = new HashMap<>();
+        env.put("TEST_ENV_VAR", "XYZ");
+
+        final CommandLine cl = new CommandLine(testScript);
+
+        final int exitValue = exec.execute(cl, env);
+
+        assertEquals("FOO.XYZ.", baos.toString().trim());
+        assertFalse(exec.isFailure(exitValue));
+    }
+
+    // ======================================================================
+    // === Long running tests
+    // ======================================================================
+
+    /**
+      * Start a process and connect stdout and stderr.
+      *
+      * @throws Exception the test failed
+      */
+     @Test
+    public void testExecuteWithStdOutErr() throws Exception {
+        final CommandLine cl = new CommandLine(testScript);
+        final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(System.out, System.err);
         final DefaultExecutor executor = new DefaultExecutor();
+        executor.setStreamHandler(pumpStreamHandler);
         final int exitValue = executor.execute(cl);
         assertFalse(exec.isFailure(exitValue));
-     }
+    }
+
+    // ======================================================================
+    // === Helper methods
+    // ======================================================================
+
+    @Test
+    public void testExecuteWithWorkingDirectory() throws Exception {
+        final File workingDir = new File("./target");
+        final CommandLine cl = new CommandLine(testScript);
+        exec.setWorkingDirectory(workingDir);
+        final int exitValue = exec.execute(cl);
+        assertEquals("FOO..", baos.toString().trim());
+        assertFalse(exec.isFailure(exitValue));
+        assertEquals(exec.getWorkingDirectory(), workingDir);
+    }
 
     /**
      * The test script reads an argument from {@code stdin} and prints
@@ -681,116 +793,4 @@ public class DefaultExecutorTest {
         final String result = baos.toString();
         assertTrue("Result '" + result + "' should contain 'Hello Foo!'", result.indexOf("Hello Foo!") >= 0);
     }
-
-    /**
-     * Call a script to dump the environment variables of the subprocess.
-     *
-     * @throws Exception the test failed
-     */
-    @Test
-    public void testEnvironmentVariables() throws Exception {
-        exec.execute(new CommandLine(environmentSript));
-        final String environment = baos.toString().trim();
-        assertFalse("Found no environment variables", environment.isEmpty());
-        assertFalse(environment.indexOf("NEW_VAR") >= 0);
-    }
-
-    /**
-     * Call a script to dump the environment variables of the subprocess
-     * after adding a custom environment variable.
-     *
-     * @throws Exception the test failed
-     */
-    @Test
-    public void testAddEnvironmentVariables() throws Exception {
-        final Map<String, String> myEnvVars = new HashMap<>(EnvironmentUtils.getProcEnvironment());
-        myEnvVars.put("NEW_VAR", "NEW_VAL");
-        exec.execute(new CommandLine(environmentSript), myEnvVars);
-        final String environment = baos.toString().trim();
-        assertTrue("Expecting NEW_VAR in " + environment, environment.indexOf("NEW_VAR") >= 0);
-        assertTrue("Expecting NEW_VAL in " + environment, environment.indexOf("NEW_VAL") >= 0);
-    }
-
-    @Test
-    public void testAddEnvironmentVariableEmbeddedQuote() throws Exception {
-        final Map<String, String> myEnvVars = new HashMap<>(EnvironmentUtils.getProcEnvironment());
-        final String name = "NEW_VAR";
-        final String value = "NEW_\"_VAL";
-        myEnvVars.put(name, value);
-        exec.execute(new CommandLine(environmentSript), myEnvVars);
-        final String environment = baos.toString().trim();
-        assertTrue("Expecting " + name + " in " + environment, environment.indexOf(name) >= 0);
-        assertTrue("Expecting " + value + " in " + environment, environment.indexOf(value) >= 0);
-    }
-
-    // ======================================================================
-    // === Long running tests
-    // ======================================================================
-
-    /**
-     * Start any processes in a loop to make sure that we do
-     * not leave any handles/resources open.
-     *
-     * @throws Exception the test failed
-     */
-    @Test
-    @Ignore
-    public void _testExecuteStability() throws Exception {
-
-        // make a plain-vanilla test
-        for (int i = 0; i < 100; i++) {
-            final Map<String, String> env = new HashMap<>();
-            env.put("TEST_ENV_VAR", Integer.toString(i));
-            final CommandLine cl = new CommandLine(testScript);
-            final int exitValue = exec.execute(cl, env);
-            assertFalse(exec.isFailure(exitValue));
-            assertEquals("FOO." + i + ".", baos.toString().trim());
-            baos.reset();
-        }
-
-        // now be nasty and use the watchdog to kill out sub-processes
-        for (int i = 0; i < 100; i++) {
-            final Map<String, String> env = new HashMap<>();
-            env.put("TEST_ENV_VAR", Integer.toString(i));
-            final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
-            final CommandLine cl = new CommandLine(foreverTestScript);
-            final ExecuteWatchdog watchdog = new ExecuteWatchdog(500);
-            exec.setWatchdog(watchdog);
-            exec.execute(cl, env, resultHandler);
-            resultHandler.waitFor(WAITFOR_TIMEOUT);
-            assertTrue("ResultHandler received a result", resultHandler.hasResult());
-            assertNotNull(resultHandler.getException());
-            baos.reset();
-        }
-    }
-
-    // ======================================================================
-    // === Helper methods
-    // ======================================================================
-
-    private String readFile(final File file) throws Exception {
-
-        String text;
-        final StringBuilder contents = new StringBuilder();
-        final BufferedReader reader = new BufferedReader(new FileReader(file));
-
-        while ((text = reader.readLine()) != null) {
-            contents.append(text).append(System.getProperty("line.separator"));
-        }
-        reader.close();
-        return contents.toString();
-    }
-
-    private int getOccurrences(final String data, final char c) {
-
-        int result = 0;
-
-        for (int i=0; i<data.length(); i++) {
-            if (data.charAt(i) == c) {
-                result++;
-            }
-        }
-
-        return result;
-    }
 }
diff --git a/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java b/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java
index e5c6fd9d..74596570 100644
--- a/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java
+++ b/src/test/java/org/apache/commons/exec/LogOutputStreamTest.java
@@ -35,24 +35,51 @@ import org.junit.Test;
  */
 public class LogOutputStreamTest {
 
-    private final Executor exec = new DefaultExecutor();
-    private final File testDir = new File("src/test/scripts");
-    private OutputStream systemOut;
-    private final File environmentScript = TestUtil.resolveScriptForOS(testDir + "/environment");
-    private final File utf8CharacterScript = TestUtil.resolveScriptForOS(testDir + "/utf8Characters");
+    private final class SystemLogOutputStream extends LogOutputStream {
+
+        StringBuffer output = new StringBuffer();
+
+        private SystemLogOutputStream(final int level) {
+            super(level);
+        }
+
+        private SystemLogOutputStream(final int level, final Charset charset) {
+            super(level, charset);
+        }
+
+        private String getOutput() {
+            return output.toString();
+        }
 
+        @Override
+        protected void processLine(final String line, final int level) {
+            System.out.println(line);
+            output.append(line);
+        }
+    }
     @BeforeClass
     public static void classSetUp() {
         // turn on debug mode and throw an exception for each encountered problem
         System.setProperty("org.apache.commons.exec.lenient", "false");
         System.setProperty("org.apache.commons.exec.debug", "true");
     }
+    private final Executor exec = new DefaultExecutor();
+    private final File testDir = new File("src/test/scripts");
+    private OutputStream systemOut;
+
+    private final File environmentScript = TestUtil.resolveScriptForOS(testDir + "/environment");
+
+    private final File utf8CharacterScript = TestUtil.resolveScriptForOS(testDir + "/utf8Characters");
 
     @Before
     public void setUp() throws Exception {
 
     }
 
+    // ======================================================================
+    // Start of regression tests
+    // ======================================================================
+
     @After
     public void tearDown() throws Exception {
         if (this.systemOut != null) {
@@ -60,10 +87,6 @@ public class LogOutputStreamTest {
         }
     }
 
-    // ======================================================================
-    // Start of regression tests
-    // ======================================================================
-
     @Test
     public void testStdout() throws Exception {
         this.systemOut = new SystemLogOutputStream(1);
@@ -74,6 +97,10 @@ public class LogOutputStreamTest {
         assertFalse(exec.isFailure(exitValue));
     }
 
+    // ======================================================================
+    // Helper classes
+    // ======================================================================
+
     @Test
     @Ignore("The file utf8CharacterScript is missing from the repository and is not in its history")
     public void testStdoutWithUTF8Characters() throws Exception {
@@ -86,31 +113,4 @@ public class LogOutputStreamTest {
         assertEquals("This string contains UTF-8 characters like the see no evil monkey \uD83D\uDE48 and the right single quotation mark \u2019", ((SystemLogOutputStream) systemOut).getOutput());
     }
 
-    // ======================================================================
-    // Helper classes
-    // ======================================================================
-
-    private final class SystemLogOutputStream extends LogOutputStream {
-
-        StringBuffer output = new StringBuffer();
-
-        private SystemLogOutputStream(final int level) {
-            super(level);
-        }
-
-        private SystemLogOutputStream(final int level, final Charset charset) {
-            super(level, charset);
-        }
-
-        @Override
-        protected void processLine(final String line, final int level) {
-            System.out.println(line);
-            output.append(line);
-        }
-
-        private String getOutput() {
-            return output.toString();
-        }
-    }
-
 }
diff --git a/src/test/java/org/apache/commons/exec/TestUtil.java b/src/test/java/org/apache/commons/exec/TestUtil.java
index ec168563..024e8a2e 100644
--- a/src/test/java/org/apache/commons/exec/TestUtil.java
+++ b/src/test/java/org/apache/commons/exec/TestUtil.java
@@ -25,37 +25,37 @@ import junit.framework.AssertionFailedError;
  */
 public final class TestUtil {
 
-    private TestUtil() {
-    }
-
-    public static File resolveScriptForOS(final String script) {
+    /**
+     * Gets success and fail return codes used by the test scripts
+     * @return int array[2] = {ok, success}
+     */
+    public static int[] getTestScriptCodesForOS() {
         if (OS.isFamilyWindows()) {
-            return new File(script + ".bat");
+            return new int[]{0,1};
         }
         if (OS.isFamilyUnix()) {
-            return new File(script + ".sh");
+            return new int[]{0,1};
         }
         if (OS.isFamilyOpenVms()) {
-            return new File(script + ".dcl");
+            return new int[]{1,2};
         }
         throw new AssertionFailedError("Test not supported for this OS");
     }
 
-    /**
-     * Gets success and fail return codes used by the test scripts
-     * @return int array[2] = {ok, success}
-     */
-    public static int[] getTestScriptCodesForOS() {
+    public static File resolveScriptForOS(final String script) {
         if (OS.isFamilyWindows()) {
-            return new int[]{0,1};
+            return new File(script + ".bat");
         }
         if (OS.isFamilyUnix()) {
-            return new int[]{0,1};
+            return new File(script + ".sh");
         }
         if (OS.isFamilyOpenVms()) {
-            return new int[]{1,2};
+            return new File(script + ".dcl");
         }
         throw new AssertionFailedError("Test not supported for this OS");
     }
 
+    private TestUtil() {
+    }
+
 }
diff --git a/src/test/java/org/apache/commons/exec/TutorialTest.java b/src/test/java/org/apache/commons/exec/TutorialTest.java
index 7961bf95..b36f157e 100644
--- a/src/test/java/org/apache/commons/exec/TutorialTest.java
+++ b/src/test/java/org/apache/commons/exec/TutorialTest.java
@@ -36,39 +36,43 @@ import org.junit.Test;
  */
 public class TutorialTest {
 
-    /** the directory to pick up the test scripts */
-    private final File testDir = new File("src/test/scripts");
-
-    /** simulates a PDF print job */
-    private final File acroRd32Script = TestUtil.resolveScriptForOS(testDir + "/acrord32");
-
-    @Test
-    public void testTutorialExample() throws Exception {
+    private final class PrintResultHandler extends DefaultExecuteResultHandler {
 
-        final long printJobTimeout = 15000;
-        final boolean printInBackground = false;
-        final File pdfFile = new File("/Documents and Settings/foo.pdf");
+        private ExecuteWatchdog watchdog;
 
-        PrintResultHandler printResult;
+        public PrintResultHandler(final ExecuteWatchdog watchdog)
+        {
+            this.watchdog = watchdog;
+        }
 
-        try {
-            // printing takes around 10 seconds
-            System.out.println("[main] Preparing print job ...");
-            printResult = print(pdfFile, printJobTimeout, printInBackground);
-            System.out.println("[main] Successfully sent the print job ...");
+        public PrintResultHandler(final int exitValue) {
+            super.onProcessComplete(exitValue);
         }
-        catch (final Exception e) {
-            e.printStackTrace();
-            fail("[main] Printing of the following document failed : " + pdfFile.getAbsolutePath());
-            throw e;
+
+        @Override
+        public void onProcessComplete(final int exitValue) {
+            super.onProcessComplete(exitValue);
+            System.out.println("[resultHandler] The document was successfully printed ...");
         }
 
-        // come back to check the print result
-        System.out.println("[main] Test is exiting but waiting for the print job to finish...");
-        printResult.waitFor();
-        System.out.println("[main] The print job has finished ...");
+        @Override
+        public void onProcessFailed(final ExecuteException e) {
+            super.onProcessFailed(e);
+            if (watchdog != null && watchdog.killedProcess()) {
+                System.err.println("[resultHandler] The print process timed out");
+            }
+            else {
+                System.err.println("[resultHandler] The print process failed to do : " + e.getMessage());
+            }
+        }
     }
 
+    /** the directory to pick up the test scripts */
+    private final File testDir = new File("src/test/scripts");
+
+    /** simulates a PDF print job */
+    private final File acroRd32Script = TestUtil.resolveScriptForOS(testDir + "/acrord32");
+
     /**
      * Simulate printing a PDF document.
      *
@@ -119,34 +123,30 @@ public class TutorialTest {
         return resultHandler;
     }
 
-    private final class PrintResultHandler extends DefaultExecuteResultHandler {
+    @Test
+    public void testTutorialExample() throws Exception {
 
-        private ExecuteWatchdog watchdog;
+        final long printJobTimeout = 15000;
+        final boolean printInBackground = false;
+        final File pdfFile = new File("/Documents and Settings/foo.pdf");
 
-        public PrintResultHandler(final ExecuteWatchdog watchdog)
-        {
-            this.watchdog = watchdog;
-        }
+        PrintResultHandler printResult;
 
-        public PrintResultHandler(final int exitValue) {
-            super.onProcessComplete(exitValue);
+        try {
+            // printing takes around 10 seconds
+            System.out.println("[main] Preparing print job ...");
+            printResult = print(pdfFile, printJobTimeout, printInBackground);
+            System.out.println("[main] Successfully sent the print job ...");
         }
-
-        @Override
-        public void onProcessComplete(final int exitValue) {
-            super.onProcessComplete(exitValue);
-            System.out.println("[resultHandler] The document was successfully printed ...");
+        catch (final Exception e) {
+            e.printStackTrace();
+            fail("[main] Printing of the following document failed : " + pdfFile.getAbsolutePath());
+            throw e;
         }
 
-        @Override
-        public void onProcessFailed(final ExecuteException e) {
-            super.onProcessFailed(e);
-            if (watchdog != null && watchdog.killedProcess()) {
-                System.err.println("[resultHandler] The print process timed out");
-            }
-            else {
-                System.err.println("[resultHandler] The print process failed to do : " + e.getMessage());
-            }
-        }
+        // come back to check the print result
+        System.out.println("[main] Test is exiting but waiting for the print job to finish...");
+        printResult.waitFor();
+        System.out.println("[main] The print job has finished ...");
     }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/commons/exec/environment/EnvironmentUtilsTest.java b/src/test/java/org/apache/commons/exec/environment/EnvironmentUtilsTest.java
index d45ef9fe..8ba06401 100644
--- a/src/test/java/org/apache/commons/exec/environment/EnvironmentUtilsTest.java
+++ b/src/test/java/org/apache/commons/exec/environment/EnvironmentUtilsTest.java
@@ -38,24 +38,19 @@ import org.junit.Test;
 public class EnvironmentUtilsTest {
 
     /**
-     * Tests the behavior of the EnvironmentUtils.toStrings()
-     * when using a {@code null} environment.
+     * Accessing environment variables is case-sensitive or not depending
+     * on the operating system but the values of the environment variable
+     * are always case-sensitive. So make sure that this assumption holds
+     * on all operating systems.
+     *
+     * @throws Exception the test failed
      */
     @Test
-    public void testToStrings() {
-        // check for a non-existing environment when passing null
-        assertNull(EnvironmentUtils.toStrings(null));
-        // check for an environment when filling in two variables
-        final Map<String, String> env = new HashMap<>();
-        assertArrayEquals(new String[0], EnvironmentUtils.toStrings(env));
-        env.put("foo2", "bar2");
-        env.put("foo", "bar");
-        final String[] envStrings = EnvironmentUtils.toStrings(env);
-        final String[] expected = {"foo2=bar2", "foo=bar"};
-        // ensure the result does not depend on the hash ordering
-        Arrays.sort(expected);
-        Arrays.sort(envStrings);
-        assertArrayEquals(expected, envStrings);
+    public void testCaseInsensitiveVariableLookup() throws Exception {
+        final Map<String, String> procEnvironment = EnvironmentUtils.getProcEnvironment();
+        // Check that case is preserved for values
+        EnvironmentUtils.addVariableToEnvironment(procEnvironment, "foo=bAr");
+        assertEquals("bAr", procEnvironment.get("foo"));
     }
 
     /**
@@ -110,19 +105,24 @@ public class EnvironmentUtilsTest {
     }
 
     /**
-     * Accessing environment variables is case-sensitive or not depending
-     * on the operating system but the values of the environment variable
-     * are always case-sensitive. So make sure that this assumption holds
-     * on all operating systems.
-     *
-     * @throws Exception the test failed
+     * Tests the behavior of the EnvironmentUtils.toStrings()
+     * when using a {@code null} environment.
      */
     @Test
-    public void testCaseInsensitiveVariableLookup() throws Exception {
-        final Map<String, String> procEnvironment = EnvironmentUtils.getProcEnvironment();
-        // Check that case is preserved for values
-        EnvironmentUtils.addVariableToEnvironment(procEnvironment, "foo=bAr");
-        assertEquals("bAr", procEnvironment.get("foo"));
+    public void testToStrings() {
+        // check for a non-existing environment when passing null
+        assertNull(EnvironmentUtils.toStrings(null));
+        // check for an environment when filling in two variables
+        final Map<String, String> env = new HashMap<>();
+        assertArrayEquals(new String[0], EnvironmentUtils.toStrings(env));
+        env.put("foo2", "bar2");
+        env.put("foo", "bar");
+        final String[] envStrings = EnvironmentUtils.toStrings(env);
+        final String[] expected = {"foo2=bar2", "foo=bar"};
+        // ensure the result does not depend on the hash ordering
+        Arrays.sort(expected);
+        Arrays.sort(envStrings);
+        assertArrayEquals(expected, envStrings);
     }
 
     /**
diff --git a/src/test/java/org/apache/commons/exec/issues/Exec36Test.java b/src/test/java/org/apache/commons/exec/issues/Exec36Test.java
index 104ad380..ccd9083c 100644
--- a/src/test/java/org/apache/commons/exec/issues/Exec36Test.java
+++ b/src/test/java/org/apache/commons/exec/issues/Exec36Test.java
@@ -47,6 +47,72 @@ public class Exec36Test {
 
     private ByteArrayOutputStream baos;
 
+    /**
+     * Some complex real-life command line from
+     * http://blogs.msdn.com/b/astebner/archive/2005/12/13/503471.aspx
+     */
+    @Test
+    @Ignore
+    public void _testExec36_4() throws Exception {
+
+        CommandLine cmdl;
+
+        final String line = "./script/jrake "
+                + "cruise:publish_installers "
+                + "INSTALLER_VERSION=unstable_2_1 "
+                + "INSTALLER_PATH=\"/var/lib/cruise-agent/installers\" "
+                + "INSTALLER_DOWNLOAD_SERVER='something'"
+                + "WITHOUT_HELP_DOC=true";
+
+        cmdl = CommandLine.parse(line);
+        final String[] args = cmdl.toStrings();
+        assertEquals("./script/jrake", args[0]);
+        assertEquals("cruise:publish_installers", args[1]);
+        assertEquals("INSTALLER_VERSION=unstable_2_1", args[2]);
+        assertEquals("INSTALLER_PATH=\"/var/lib/cruise-agent/installers\"", args[3]);
+        assertEquals("INSTALLER_DOWNLOAD_SERVER='something'", args[4]);
+        assertEquals("WITHOUT_HELP_DOC=true", args[5]);
+    }
+
+    /**
+     * Some complex real-life command line from
+     * http://blogs.msdn.com/b/astebner/archive/2005/12/13/503471.aspx
+     */
+    @Test
+    @Ignore
+    public void _testExec36_5() {
+
+        CommandLine cmdl;
+
+        final String line = "dotnetfx.exe"
+                + " /q:a "
+                + "/c:\"install.exe /l \"\"c:\\Documents and Settings\\myusername\\Local Settings\\Temp\\netfx.log\"\" /q\"";
+
+        cmdl = CommandLine.parse(line);
+        final String[] args = cmdl.toStrings();
+        assertEquals("dotnetfx.exe", args[0]);
+        assertEquals("/q:a", args[1]);
+        assertEquals("/c:\"install.exe /l \"\"c:\\Documents and Settings\\myusername\\Local Settings\\Temp\\netfx.log\"\" /q\"", args[2] );
+    }
+
+    /**
+     * Test the following command line
+     *
+     * C:\CVS_DB\WeightsEngine /f WeightsEngine.mak CFG="WeightsEngine - Win32Release"
+     */
+    @Test
+    @Ignore
+    public void _testExec36_6() {
+
+        final String commandline = "C:\\CVS_DB\\WeightsEngine /f WeightsEngine.mak CFG=\"WeightsEngine - Win32Release\"";
+
+        final CommandLine cmdl = CommandLine.parse(commandline);
+        final String[] args = cmdl.getArguments();
+        assertEquals("/f", args[0]);
+        assertEquals("WeightsEngine.mak", args[1]);
+        assertEquals("CFG=\"WeightsEngine - Win32Release\"", args[2]);
+    }
+
     @Before
     public void setUp() throws Exception {
         // prepare a ready to Executor
@@ -153,70 +219,4 @@ public class Exec36Test {
             assertEquals(expected, result);
         }
     }
-
-    /**
-     * Some complex real-life command line from
-     * http://blogs.msdn.com/b/astebner/archive/2005/12/13/503471.aspx
-     */
-    @Test
-    @Ignore
-    public void _testExec36_4() throws Exception {
-
-        CommandLine cmdl;
-
-        final String line = "./script/jrake "
-                + "cruise:publish_installers "
-                + "INSTALLER_VERSION=unstable_2_1 "
-                + "INSTALLER_PATH=\"/var/lib/cruise-agent/installers\" "
-                + "INSTALLER_DOWNLOAD_SERVER='something'"
-                + "WITHOUT_HELP_DOC=true";
-
-        cmdl = CommandLine.parse(line);
-        final String[] args = cmdl.toStrings();
-        assertEquals("./script/jrake", args[0]);
-        assertEquals("cruise:publish_installers", args[1]);
-        assertEquals("INSTALLER_VERSION=unstable_2_1", args[2]);
-        assertEquals("INSTALLER_PATH=\"/var/lib/cruise-agent/installers\"", args[3]);
-        assertEquals("INSTALLER_DOWNLOAD_SERVER='something'", args[4]);
-        assertEquals("WITHOUT_HELP_DOC=true", args[5]);
-    }
-
-    /**
-     * Some complex real-life command line from
-     * http://blogs.msdn.com/b/astebner/archive/2005/12/13/503471.aspx
-     */
-    @Test
-    @Ignore
-    public void _testExec36_5() {
-
-        CommandLine cmdl;
-
-        final String line = "dotnetfx.exe"
-                + " /q:a "
-                + "/c:\"install.exe /l \"\"c:\\Documents and Settings\\myusername\\Local Settings\\Temp\\netfx.log\"\" /q\"";
-
-        cmdl = CommandLine.parse(line);
-        final String[] args = cmdl.toStrings();
-        assertEquals("dotnetfx.exe", args[0]);
-        assertEquals("/q:a", args[1]);
-        assertEquals("/c:\"install.exe /l \"\"c:\\Documents and Settings\\myusername\\Local Settings\\Temp\\netfx.log\"\" /q\"", args[2] );
-    }
-
-    /**
-     * Test the following command line
-     *
-     * C:\CVS_DB\WeightsEngine /f WeightsEngine.mak CFG="WeightsEngine - Win32Release"
-     */
-    @Test
-    @Ignore
-    public void _testExec36_6() {
-
-        final String commandline = "C:\\CVS_DB\\WeightsEngine /f WeightsEngine.mak CFG=\"WeightsEngine - Win32Release\"";
-
-        final CommandLine cmdl = CommandLine.parse(commandline);
-        final String[] args = cmdl.getArguments();
-        assertEquals("/f", args[0]);
-        assertEquals("WeightsEngine.mak", args[1]);
-        assertEquals("CFG=\"WeightsEngine - Win32Release\"", args[2]);
-    }
 }
diff --git a/src/test/java/org/apache/commons/exec/issues/Exec41Test.java b/src/test/java/org/apache/commons/exec/issues/Exec41Test.java
index f2e635ad..6f2bde5f 100644
--- a/src/test/java/org/apache/commons/exec/issues/Exec41Test.java
+++ b/src/test/java/org/apache/commons/exec/issues/Exec41Test.java
@@ -38,6 +38,53 @@ public class Exec41Test {
     private final File testDir = new File("src/test/scripts");
     private final File pingScript = TestUtil.resolveScriptForOS(testDir + "/ping");
 
+    /**
+     * Test EXEC-41 with a disabled PumpStreamHandler to check if we could return
+     * immediately after killing the process (no streams implies no blocking
+     * stream pumper threads). But you have to be 100% sure that the subprocess
+     * is not writing to 'stdout' and 'stderr'.
+     *
+     * For this test we are using the batch file - under Windows the 'ping'
+     * process can't be killed (not supported by Win32) and will happily
+     * run the given time (e.g. 10 seconds) even hwen the batch file is already
+     * killed.
+     *
+     * @throws Exception the test failed
+     */
+    @Test
+    public void testExec41WithoutStreams() throws Exception {
+
+        final CommandLine cmdLine = new CommandLine(pingScript);
+        cmdLine.addArgument("10"); // sleep 10 seconds
+        final DefaultExecutor executor = new DefaultExecutor();
+        final ExecuteWatchdog watchdog = new ExecuteWatchdog(2*1000); // allow process no more than 2 seconds
+
+        // create a custom "PumpStreamHandler" doing no pumping at all
+        final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(null, null, null);
+
+        executor.setWatchdog(watchdog);
+        executor.setStreamHandler(pumpStreamHandler);
+
+        final long startTime = System.currentTimeMillis();
+
+        try {
+            executor.execute(cmdLine);
+        } catch (final ExecuteException e) {
+            System.out.println(e);
+        }
+
+        final long duration = System.currentTimeMillis() - startTime;
+
+        System.out.println("Process completed in " + duration +" millis; below is its output");
+
+        if (watchdog.killedProcess()) {
+            System.out.println("Process timed out and was killed.");
+        }
+
+        assertTrue("The process was killed by the watchdog", watchdog.killedProcess());
+        assertTrue("Skipping the Thread.join() did not work, duration="+duration, duration < 9000);
+    }
+
     /**
      *
      * When a process runs longer than allowed by a configured watchdog's
@@ -94,51 +141,4 @@ public class Exec41Test {
         assertTrue("The process was killed by the watchdog", watchdog.killedProcess());
         assertTrue("Skipping the Thread.join() did not work", duration < 9000);
     }
-
-    /**
-     * Test EXEC-41 with a disabled PumpStreamHandler to check if we could return
-     * immediately after killing the process (no streams implies no blocking
-     * stream pumper threads). But you have to be 100% sure that the subprocess
-     * is not writing to 'stdout' and 'stderr'.
-     *
-     * For this test we are using the batch file - under Windows the 'ping'
-     * process can't be killed (not supported by Win32) and will happily
-     * run the given time (e.g. 10 seconds) even hwen the batch file is already
-     * killed.
-     *
-     * @throws Exception the test failed
-     */
-    @Test
-    public void testExec41WithoutStreams() throws Exception {
-
-        final CommandLine cmdLine = new CommandLine(pingScript);
-        cmdLine.addArgument("10"); // sleep 10 seconds
-        final DefaultExecutor executor = new DefaultExecutor();
-        final ExecuteWatchdog watchdog = new ExecuteWatchdog(2*1000); // allow process no more than 2 seconds
-
-        // create a custom "PumpStreamHandler" doing no pumping at all
-        final PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(null, null, null);
-
-        executor.setWatchdog(watchdog);
-        executor.setStreamHandler(pumpStreamHandler);
-
-        final long startTime = System.currentTimeMillis();
-
-        try {
-            executor.execute(cmdLine);
-        } catch (final ExecuteException e) {
-            System.out.println(e);
-        }
-
-        final long duration = System.currentTimeMillis() - startTime;
-
-        System.out.println("Process completed in " + duration +" millis; below is its output");
-
-        if (watchdog.killedProcess()) {
-            System.out.println("Process timed out and was killed.");
-        }
-
-        assertTrue("The process was killed by the watchdog", watchdog.killedProcess());
-        assertTrue("Skipping the Thread.join() did not work, duration="+duration, duration < 9000);
-    }
 }
diff --git a/src/test/java/org/apache/commons/exec/issues/Exec62Test.java b/src/test/java/org/apache/commons/exec/issues/Exec62Test.java
index 4fe0def6..cbbee4f0 100644
--- a/src/test/java/org/apache/commons/exec/issues/Exec62Test.java
+++ b/src/test/java/org/apache/commons/exec/issues/Exec62Test.java
@@ -40,24 +40,6 @@ import org.junit.Test;
 public class Exec62Test {
     private Path outputFile;
 
-    @Before
-    public void setUp() throws Exception {
-        outputFile = Files.createTempFile("foo", ".log");
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        Files.delete(outputFile);
-    }
-
-    @Ignore("Test behaves differently between Mac OS X and Linux - don't know why")
-    @Test(expected = TimeoutException.class, timeout = 10000)
-    public void testMe() throws Exception {
-        if (OS.isFamilyUnix()) {
-            execute("exec-62");
-        }
-    }
-
     private void execute(final String scriptName) throws Exception {
         final ExecuteWatchdog watchdog = new ExecuteWatchdog(4000);
         final CommandLine commandLine = new CommandLine("/bin/sh");
@@ -78,4 +60,22 @@ public class Exec62Test {
             throw new TimeoutException(String.format("Transcode process was killed on timeout %1$s ms, command line %2$s", 4000, commandLine.toString()));
         }
     }
+
+    @Before
+    public void setUp() throws Exception {
+        outputFile = Files.createTempFile("foo", ".log");
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        Files.delete(outputFile);
+    }
+
+    @Ignore("Test behaves differently between Mac OS X and Linux - don't know why")
+    @Test(expected = TimeoutException.class, timeout = 10000)
+    public void testMe() throws Exception {
+        if (OS.isFamilyUnix()) {
+            execute("exec-62");
+        }
+    }
 }
diff --git a/src/test/java/org/apache/commons/exec/issues/Exec65Test.java b/src/test/java/org/apache/commons/exec/issues/Exec65Test.java
index 6da82bc7..d97e710f 100644
--- a/src/test/java/org/apache/commons/exec/issues/Exec65Test.java
+++ b/src/test/java/org/apache/commons/exec/issues/Exec65Test.java
@@ -38,22 +38,6 @@ import java.io.File;
  */
 public class Exec65Test extends AbstractExecTest {
 
-    @Test(expected = ExecuteException.class, timeout = TEST_TIMEOUT)
-    public void testExec65WitSleepUsingSleepCommandDirectly() throws Exception {
-
-        if (!OS.isFamilyUnix()) {
-            throw new ExecuteException(testNotSupportedForCurrentOperatingSystem(), 0);
-        }
-        final ExecuteWatchdog watchdog = new ExecuteWatchdog(WATCHDOG_TIMEOUT);
-        final DefaultExecutor executor = new DefaultExecutor();
-        final CommandLine command = new CommandLine("sleep");
-        command.addArgument("60");
-        executor.setStreamHandler(new PumpStreamHandler(System.out, System.err));
-        executor.setWatchdog(watchdog);
-
-        executor.execute(command);
-    }
-
     /**
      * This test currently only works for Mac OS X
      * <ul>
@@ -116,4 +100,20 @@ public class Exec65Test extends AbstractExecTest {
 
         executor.execute(command);
     }
+
+    @Test(expected = ExecuteException.class, timeout = TEST_TIMEOUT)
+    public void testExec65WitSleepUsingSleepCommandDirectly() throws Exception {
+
+        if (!OS.isFamilyUnix()) {
+            throw new ExecuteException(testNotSupportedForCurrentOperatingSystem(), 0);
+        }
+        final ExecuteWatchdog watchdog = new ExecuteWatchdog(WATCHDOG_TIMEOUT);
+        final DefaultExecutor executor = new DefaultExecutor();
+        final CommandLine command = new CommandLine("sleep");
+        command.addArgument("60");
+        executor.setStreamHandler(new PumpStreamHandler(System.out, System.err));
+        executor.setWatchdog(watchdog);
+
+        executor.execute(command);
+    }
 }
diff --git a/src/test/java/org/apache/commons/exec/util/StringUtilTest.java b/src/test/java/org/apache/commons/exec/util/StringUtilTest.java
index 3af7925e..7abd7739 100644
--- a/src/test/java/org/apache/commons/exec/util/StringUtilTest.java
+++ b/src/test/java/org/apache/commons/exec/util/StringUtilTest.java
@@ -29,31 +29,30 @@ import org.junit.Test;
  */
 public class StringUtilTest {
     /**
-     * Test no string substitution
+     * Test a default string substitution, e.g. all placeholders
+     * are expanded.
      */
     @Test
-    public void testNoStringSubstitution() throws Exception
+    public void testDefaultStringSubstitution() throws Exception
     {
         final Map<String, String> vars = new HashMap<>();
         vars.put("foo", "FOO");
         vars.put("bar", "BAR");
 
-        assertEquals("This is a FOO & BAR test", StringUtils.stringSubstitution("This is a FOO & BAR test", vars, true).toString());
+        assertEquals("This is a FOO & BAR test", StringUtils.stringSubstitution("This is a ${foo} & ${bar} test", vars, true).toString());
+        assertEquals("This is a FOO & BAR test", StringUtils.stringSubstitution("This is a ${foo} & ${bar} test", vars, false).toString());
     }
 
     /**
-     * Test a default string substitution, e.g. all placeholders
-     * are expanded.
+     * Test a erroneous template.
      */
     @Test
-    public void testDefaultStringSubstitution() throws Exception
+    public void testErroneousTemplate() throws Exception
     {
         final Map<String, String> vars = new HashMap<>();
         vars.put("foo", "FOO");
-        vars.put("bar", "BAR");
 
-        assertEquals("This is a FOO & BAR test", StringUtils.stringSubstitution("This is a ${foo} & ${bar} test", vars, true).toString());
-        assertEquals("This is a FOO & BAR test", StringUtils.stringSubstitution("This is a ${foo} & ${bar} test", vars, false).toString());
+        assertEquals("This is a FOO & ${}} test",  StringUtils.stringSubstitution("This is a ${foo} & ${}} test", vars, true).toString());
     }
 
     /**
@@ -80,14 +79,15 @@ public class StringUtilTest {
     }
 
     /**
-     * Test a erroneous template.
+     * Test no string substitution
      */
     @Test
-    public void testErroneousTemplate() throws Exception
+    public void testNoStringSubstitution() throws Exception
     {
         final Map<String, String> vars = new HashMap<>();
         vars.put("foo", "FOO");
+        vars.put("bar", "BAR");
 
-        assertEquals("This is a FOO & ${}} test",  StringUtils.stringSubstitution("This is a ${foo} & ${}} test", vars, true).toString());
+        assertEquals("This is a FOO & BAR test", StringUtils.stringSubstitution("This is a FOO & BAR test", vars, true).toString());
     }
 }
\ No newline at end of file