CommandlineTest.java
package org.codehaus.plexus.util.cli;
/*
* Copyright The Codehaus Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.Os;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.cli.shell.BourneShell;
import org.codehaus.plexus.util.cli.shell.CmdShell;
import org.codehaus.plexus.util.cli.shell.Shell;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/**
* <p>CommandlineTest class.</p>
*
* @author herve
* @version $Id: $Id
* @since 3.4.0
*/
class CommandlineTest {
private String baseDir;
/**
* <p>setUp.</p>
*
* @throws java.lang.Exception if any.
*/
@BeforeEach
void setUp() throws Exception {
baseDir = System.getProperty("basedir");
if (baseDir == null) {
baseDir = new File(".").getCanonicalPath();
}
}
/**
* <p>testCommandlineWithoutCommandInConstructor.</p>
*/
@Test
void commandlineWithoutCommandInConstructor() {
Commandline cmd = new Commandline(new Shell());
cmd.setWorkingDirectory(baseDir);
cmd.createArgument().setValue("cd");
cmd.createArgument().setValue(".");
// NOTE: cmd.toString() uses CommandLineUtils.toString( String[] ), which *quotes* the result.
assertEquals("cd .", cmd.toString());
}
/**
* <p>testCommandlineWithCommandInConstructor.</p>
*/
@Test
void commandlineWithCommandInConstructor() {
Commandline cmd = new Commandline("cd .", new Shell());
cmd.setWorkingDirectory(baseDir);
// NOTE: cmd.toString() uses CommandLineUtils.toString( String[] ), which *quotes* the result.
assertEquals("cd .", cmd.toString());
}
/**
* <p>testExecuteBinaryOnPath.</p>
*
* @throws java.lang.Exception if any.
*/
@Test
void executeBinaryOnPath() throws Exception {
// Maven startup script on PATH is required for this test
Commandline cmd = new Commandline();
cmd.setWorkingDirectory(baseDir);
cmd.setExecutable("mvn");
assertEquals("mvn", cmd.getShell().getOriginalExecutable());
cmd.createArg().setValue("-version");
Process process = cmd.execute();
String out = IOUtil.toString(process.getInputStream());
assertTrue(out.contains("Apache Maven"));
assertTrue(out.contains("Maven home:"));
assertTrue(out.contains("Java version:"));
}
/**
* <p>testExecute.</p>
*
* @throws java.lang.Exception if any.
*/
@Test
void execute() throws Exception {
// allow it to detect the proper shell here.
Commandline cmd = new Commandline();
cmd.setWorkingDirectory(baseDir);
cmd.setExecutable("echo");
assertEquals("echo", cmd.getShell().getOriginalExecutable());
cmd.createArgument().setValue("Hello");
Process process = cmd.execute();
assertEquals("Hello", IOUtil.toString(process.getInputStream()).trim());
}
/**
* <p>testSetLine.</p>
*/
@Test
void setLine() {
Commandline cmd = new Commandline(new Shell());
cmd.setWorkingDirectory(baseDir);
cmd.setExecutable("echo");
cmd.createArgument().setLine(null);
cmd.createArgument().setLine("Hello");
// NOTE: cmd.toString() uses CommandLineUtils.toString( String[] ), which *quotes* the result.
assertEquals("echo Hello", cmd.toString());
}
/**
* <p>testCreateCommandInReverseOrder.</p>
*/
@Test
void createCommandInReverseOrder() {
Commandline cmd = new Commandline(new Shell());
cmd.setWorkingDirectory(baseDir);
cmd.createArgument().setValue(".");
cmd.createArgument(true).setValue("cd");
// NOTE: cmd.toString() uses CommandLineUtils.toString( String[] ), which *quotes* the result.
assertEquals("cd .", cmd.toString());
}
/**
* <p>testSetFile.</p>
*/
@Test
void setFile() {
Commandline cmd = new Commandline(new Shell());
cmd.setWorkingDirectory(baseDir);
cmd.createArgument().setValue("more");
File f = new File("test.txt");
cmd.createArgument().setFile(f);
String fileName = f.getAbsolutePath();
if (fileName.contains(" ")) {
fileName = "\"" + fileName + "\"";
}
// NOTE: cmd.toString() uses CommandLineUtils.toString( String[] ), which *quotes* the result.
assertEquals("more " + fileName, cmd.toString());
}
/**
* <p>testGetShellCommandLineWindows.</p>
*
* @throws java.lang.Exception if any.
*/
@Test
void getShellCommandLineWindows() throws Exception {
Commandline cmd = new Commandline(new CmdShell());
cmd.setExecutable("c:\\Program Files\\xxx");
cmd.addArguments(new String[] {"a", "b"});
String[] shellCommandline = cmd.getShellCommandline();
assertEquals(5, shellCommandline.length, "Command line size");
assertEquals("cmd.exe", shellCommandline[0]);
assertEquals("/X", shellCommandline[1]);
assertEquals("/D", shellCommandline[2]);
assertEquals("/C", shellCommandline[3]);
String expectedShellCmd = "\"c:" + File.separator + "Program Files" + File.separator + "xxx\" a b";
expectedShellCmd = "\"" + expectedShellCmd + "\"";
assertEquals(expectedShellCmd, shellCommandline[4]);
}
/**
* <p>testGetShellCommandLineWindowsWithSeveralQuotes.</p>
*
* @throws java.lang.Exception if any.
*/
@Test
void getShellCommandLineWindowsWithSeveralQuotes() throws Exception {
Commandline cmd = new Commandline(new CmdShell());
cmd.setExecutable("c:\\Program Files\\xxx");
cmd.addArguments(new String[] {"c:\\Documents and Settings\\whatever", "b"});
String[] shellCommandline = cmd.getShellCommandline();
assertEquals(5, shellCommandline.length, "Command line size");
assertEquals("cmd.exe", shellCommandline[0]);
assertEquals("/X", shellCommandline[1]);
assertEquals("/D", shellCommandline[2]);
assertEquals("/C", shellCommandline[3]);
String expectedShellCmd = "\"c:" + File.separator + "Program Files" + File.separator
+ "xxx\" \"c:\\Documents and Settings\\whatever\" b";
expectedShellCmd = "\"" + expectedShellCmd + "\"";
assertEquals(expectedShellCmd, shellCommandline[4]);
}
/**
* Test the command line generated for the bash shell
*
* @throws java.lang.Exception
*/
@Test
void getShellCommandLineBash() throws Exception {
Commandline cmd = new Commandline(new BourneShell());
cmd.setExecutable("/bin/echo");
cmd.addArguments(new String[] {"hello world"});
String[] shellCommandline = cmd.getShellCommandline();
assertEquals(3, shellCommandline.length, "Command line size");
assertEquals("/bin/sh", shellCommandline[0]);
assertEquals("-c", shellCommandline[1]);
String expectedShellCmd = "'/bin/echo' 'hello world'";
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
expectedShellCmd = "'\\bin\\echo' 'hello world'";
}
assertEquals(expectedShellCmd, shellCommandline[2]);
}
/**
* Test the command line generated for the bash shell
*
* @throws java.lang.Exception
*/
@Test
void getShellCommandLineBashWithWorkingDirectory() throws Exception {
Commandline cmd = new Commandline(new BourneShell());
cmd.setExecutable("/bin/echo");
cmd.addArguments(new String[] {"hello world"});
File root = File.listRoots()[0];
File workingDirectory = new File(root, "path with spaces");
cmd.setWorkingDirectory(workingDirectory);
String[] shellCommandline = cmd.getShellCommandline();
assertEquals(3, shellCommandline.length, "Command line size");
assertEquals("/bin/sh", shellCommandline[0]);
assertEquals("-c", shellCommandline[1]);
String expectedShellCmd = "cd '" + root.getAbsolutePath() + "path with spaces' && '/bin/echo' 'hello world'";
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
expectedShellCmd = "cd '" + root.getAbsolutePath() + "path with spaces' && '\\bin\\echo' 'hello world'";
}
assertEquals(expectedShellCmd, shellCommandline[2]);
}
/**
* Test the command line generated for the bash shell
*
* @throws java.lang.Exception
*/
@Test
void getShellCommandLineBashWithSingleQuotedArg() throws Exception {
Commandline cmd = new Commandline(new BourneShell());
cmd.setExecutable("/bin/echo");
cmd.addArguments(new String[] {"'hello world'"});
String[] shellCommandline = cmd.getShellCommandline();
assertEquals(3, shellCommandline.length, "Command line size");
assertEquals("/bin/sh", shellCommandline[0]);
assertEquals("-c", shellCommandline[1]);
String expectedShellCmd = "'/bin/echo' ''\"'\"'hello world'\"'\"''";
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
expectedShellCmd = expectedShellCmd.replace("/bin/echo", "\\bin\\echo");
}
assertEquals(expectedShellCmd, shellCommandline[2]);
}
/**
* <p>testGetShellCommandLineNonWindows.</p>
*
* @throws java.lang.Exception if any.
*/
@Test
void getShellCommandLineNonWindows() throws Exception {
Commandline cmd = new Commandline(new BourneShell());
cmd.setExecutable("/usr/bin");
cmd.addArguments(new String[] {"a", "b"});
String[] shellCommandline = cmd.getShellCommandline();
assertEquals(3, shellCommandline.length, "Command line size");
assertEquals("/bin/sh", shellCommandline[0]);
assertEquals("-c", shellCommandline[1]);
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
assertEquals("'\\usr\\bin' 'a' 'b'", shellCommandline[2]);
} else {
assertEquals("'/usr/bin' 'a' 'b'", shellCommandline[2]);
}
}
/**
* <p>testEnvironment.</p>
*
* @throws java.lang.Exception if any.
*/
@Test
void environment() throws Exception {
Commandline cmd = new Commandline();
cmd.addEnvironment("name", "value");
assertEquals("name=value", cmd.getEnvironmentVariables()[0]);
}
/**
* <p>testEnvironmentWitOverrideSystemEnvironment.</p>
*
* @throws java.lang.Exception if any.
*/
@Test
void environmentWitOverrideSystemEnvironment() throws Exception {
Commandline cmd = new Commandline();
cmd.addSystemEnvironment();
cmd.addEnvironment("JAVA_HOME", "/usr/jdk1.5");
String[] environmentVariables = cmd.getEnvironmentVariables();
for (String environmentVariable : environmentVariables) {
if ("JAVA_HOME=/usr/jdk1.5".equals(environmentVariable)) {
return;
}
}
fail("can't find JAVA_HOME=/usr/jdk1.5");
}
/**
* Test an executable with a single apostrophe <code>'</code> in its path
*
* @throws java.lang.Exception
*/
@Test
void quotedPathWithSingleApostrophe() throws Exception {
File dir = new File(System.getProperty("basedir"), "target/test/quotedpath'test");
createAndCallScript(dir, "echo Quoted");
dir = new File(System.getProperty("basedir"), "target/test/quoted path'test");
createAndCallScript(dir, "echo Quoted");
}
/**
* Test an executable with shell-expandable content in its path.
*
* @throws java.lang.Exception
*/
@Test
void pathWithShellExpansionStrings() throws Exception {
File dir = new File(System.getProperty("basedir"), "target/test/dollar$test");
createAndCallScript(dir, "echo Quoted");
}
/**
* Test an executable with a single quotation mark <code>\"</code> in its path only for non Windows box.
*
* @throws java.lang.Exception
*/
@Test
void quotedPathWithQuotationMark() throws Exception {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
System.out.println("testQuotedPathWithQuotationMark() skipped on Windows");
return;
}
File dir = new File(System.getProperty("basedir"), "target/test/quotedpath\"test");
createAndCallScript(dir, "echo Quoted");
dir = new File(System.getProperty("basedir"), "target/test/quoted path\"test");
createAndCallScript(dir, "echo Quoted");
}
/**
* Test an executable with a single quotation mark <code>\"</code> and <code>'</code> in its path only for non
* Windows box.
*
* @throws java.lang.Exception
*/
@Test
void quotedPathWithQuotationMarkAndApostrophe() throws Exception {
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
System.out.println("testQuotedPathWithQuotationMarkAndApostrophe() skipped on Windows");
return;
}
File dir = new File(System.getProperty("basedir"), "target/test/quotedpath\"'test");
createAndCallScript(dir, "echo Quoted");
dir = new File(System.getProperty("basedir"), "target/test/quoted path\"'test");
createAndCallScript(dir, "echo Quoted");
}
/**
* Test an executable with a quote in its path and no space
*
* @throws java.lang.Exception
*/
@Test
void onlyQuotedPath() throws Exception {
File dir = new File(System.getProperty("basedir"), "target/test/quotedpath'test");
File javaHome = new File(System.getProperty("java.home"));
File java;
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
java = new File(javaHome, "/bin/java.exe");
} else {
java = new File(javaHome, "/bin/java");
}
if (!java.exists()) {
throw new IOException(java.getAbsolutePath() + " doesn't exist");
}
String javaBinStr = java.getAbsolutePath();
if (Os.isFamily(Os.FAMILY_WINDOWS) && javaBinStr.contains(" ")) {
javaBinStr = "\"" + javaBinStr + "\"";
}
createAndCallScript(dir, javaBinStr + " -version");
}
/**
* <p>testDollarSignInArgumentPath.</p>
*
* @throws java.lang.Exception if any.
*/
@Test
void dollarSignInArgumentPath() throws Exception {
File dir = new File(System.getProperty("basedir"), "target/test");
if (!dir.exists()) {
assertTrue(dir.mkdirs(), "Can't create dir:" + dir.getAbsolutePath());
}
Writer writer = null;
try {
writer = Files.newBufferedWriter(dir.toPath().resolve("test$1.txt"));
IOUtil.copy("Success", writer);
} finally {
IOUtil.close(writer);
}
Commandline cmd = new Commandline();
// cmd.getShell().setShellCommand( "/bin/sh" );
cmd.getShell().setQuotedArgumentsEnabled(true);
cmd.setExecutable("cat");
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
cmd.setExecutable("dir");
}
cmd.setWorkingDirectory(dir);
cmd.createArg().setLine("test$1.txt");
executeCommandLine(cmd);
}
/**
* <p>testTimeOutException.</p>
*
* @throws java.lang.Exception if any.
*/
@Test
void timeOutException() throws Exception {
File javaHome = new File(System.getProperty("java.home"));
File java;
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
java = new File(javaHome, "/bin/java.exe");
} else {
java = new File(javaHome, "/bin/java");
}
if (!java.exists()) {
throw new IOException(java.getAbsolutePath() + " doesn't exist");
}
Commandline cli = new Commandline();
cli.setExecutable(java.getAbsolutePath());
cli.createArg().setLine("-version");
CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
try {
// if the os is faster than 1s to execute java -version the unit will fail :-)
CommandLineUtils.executeCommandLine(cli, new DefaultConsumer(), err, 1);
} catch (CommandLineTimeOutException e) {
// it works
}
}
/**
* Make the file executable for Unix box.
*
* @param path not null
* @throws IOException if any
*/
private static void makeExecutable(File path) throws IOException {
if (path == null) {
throw new IllegalArgumentException("The file is null");
}
if (!path.isFile()) {
throw new IllegalArgumentException("The file '" + path.getAbsolutePath() + "' should be a file");
}
if (!Os.isFamily(Os.FAMILY_WINDOWS)) {
Process proc = Runtime.getRuntime().exec(new String[] {"chmod", "a+x", path.getAbsolutePath()});
while (true) {
try {
proc.waitFor();
break;
} catch (InterruptedException e) {
// ignore
}
}
}
}
/**
* Create and execute a script file in the given dir with the given content. The script file will be called
* <code>echo.bat</code> for Windows box, otherwise <code>echo</code>.
*
* @param dir the parent dir where echo.bat or echo will be created
* @param content the content of the script file
* @throws Exception if any
*/
private static void createAndCallScript(File dir, String content) throws Exception {
if (!dir.exists()) {
assertTrue(dir.mkdirs(), "Can't create dir:" + dir.getAbsolutePath());
}
// Create a script file
File bat;
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
bat = new File(dir, "echo.bat");
} else {
bat = new File(dir, "echo");
}
Writer w = Files.newBufferedWriter(bat.toPath());
try {
IOUtil.copy(content, w);
} finally {
IOUtil.close(w);
}
// Change permission
makeExecutable(bat);
Commandline cmd = new Commandline();
cmd.setExecutable(bat.getAbsolutePath());
cmd.setWorkingDirectory(dir);
// Execute the script file
executeCommandLine(cmd);
}
/**
* Execute the command line
*
* @param cmd not null
* @throws Exception if any
*/
private static void executeCommandLine(Commandline cmd) throws Exception {
CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
try {
System.out.println("Command line is: " + StringUtils.join(cmd.getShellCommandline(), " "));
int exitCode = CommandLineUtils.executeCommandLine(cmd, new DefaultConsumer(), err);
if (exitCode != 0) {
throw new Exception("Exit code: " + exitCode + " - " + err.getOutput());
}
} catch (CommandLineException e) {
throw new Exception("Unable to execute command: " + e.getMessage(), e);
}
}
}