2 * Copyright 2000-2015 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.execution;
18 import com.intellij.execution.configurations.GeneralCommandLine;
19 import com.intellij.execution.util.ExecUtil;
20 import com.intellij.openapi.application.PathManager;
21 import com.intellij.openapi.util.SystemInfo;
22 import com.intellij.openapi.util.io.FileUtil;
23 import com.intellij.openapi.util.text.StringUtil;
24 import com.intellij.util.ArrayUtil;
25 import org.jetbrains.annotations.NotNull;
26 import org.jetbrains.annotations.Nullable;
27 import org.junit.Test;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.net.URISyntaxException;
36 import static com.intellij.openapi.util.Pair.pair;
37 import static com.intellij.util.containers.ContainerUtil.newHashMap;
38 import static org.junit.Assert.*;
39 import static org.junit.Assume.assumeTrue;
41 public class GeneralCommandLineTest {
43 private static final String[] ARGUMENTS = {
46 "\"quoted with spaces\"",
52 "space \"and \"quotes\" inside",
53 "\"space \"and \"quotes\" inside\"",
56 // "two trailing slashes\\\\" /* doesn't work on Windows*/
60 public void printCommandLine() {
61 GeneralCommandLine commandLine = new GeneralCommandLine();
62 commandLine.setExePath("e x e path");
63 commandLine.addParameter("with space");
64 commandLine.addParameter("\"quoted\"");
65 commandLine.addParameter("\"quoted with spaces\"");
66 commandLine.addParameters("param 1", "param2");
67 commandLine.addParameter("trailing slash\\");
68 assertEquals("\"e x e path\"" +
71 " \"\\\"quoted with spaces\\\"\"" +
74 " \"trailing slash\\\"",
75 commandLine.getCommandLineString());
79 public void unicodePath() throws Exception {
80 String mark = String.valueOf(new Random().nextInt());
82 if (SystemInfo.isWindows) {
83 script = ExecUtil.createTempExecutableScript(
84 "path with spaces 'and quotes' и юникодом ", ".cmd",
85 "@echo " + mark + "\n"
89 script = ExecUtil.createTempExecutableScript(
90 "path with spaces 'and quotes' и юникодом ", ".sh",
91 "#!/bin/sh\n" + "echo " + mark + "\n"
96 GeneralCommandLine commandLine = new GeneralCommandLine(script.getPath());
97 String output = execAndGetOutput(commandLine, null);
98 assertEquals(mark + "\n", StringUtil.convertLineSeparators(output));
101 FileUtil.delete(script);
106 public void unicodeClassPath() throws Exception {
107 assumeTrue(SystemInfo.isUnix);
109 File dir = FileUtil.createTempDirectory("path with spaces 'and quotes' и юникодом ", ".tmp");
111 GeneralCommandLine commandLine = makeJavaCommand(ParamPassingTest.class, dir);
112 String output = execAndGetOutput(commandLine, null);
113 assertEquals("=====\n=====\n", StringUtil.convertLineSeparators(output));
116 FileUtil.delete(dir);
121 public void testPassingArgumentsToJavaApp() throws Exception {
122 GeneralCommandLine commandLine = makeJavaCommand(ParamPassingTest.class, null);
123 String[] args = ArrayUtil.mergeArrays(ARGUMENTS, "&<>()@^|", "\"&<>()@^|\"");
124 commandLine.addParameters(args);
125 String output = execAndGetOutput(commandLine, null);
126 assertParamPassingTestOutput(output, args);
130 public void testPassingArgumentsToJavaAppThroughWinShell() throws Exception {
131 assumeTrue(SystemInfo.isWindows);
132 // passing "^" argument doesn't work for cmd.exe
133 String[] args = ARGUMENTS;
134 GeneralCommandLine commandLine = makeJavaCommand(ParamPassingTest.class, null);
135 String oldExePath = commandLine.getExePath();
136 commandLine.setExePath("cmd.exe");
137 // the test will fails if "call" is omitted
138 commandLine.getParametersList().prependAll("/D", "/C", "call", oldExePath);
139 commandLine.addParameters(args);
140 String output = execAndGetOutput(commandLine, null);
141 assertParamPassingTestOutput(output, args);
145 public void testPassingArgumentsToJavaAppThroughCmdScriptAndWinShell() throws Exception {
146 assumeTrue(SystemInfo.isWindows);
147 // passing "^" argument doesn't work for cmd.exe
148 String[] args = ARGUMENTS;
149 File cmdScript = createCmdFileLaunchingJavaApp();
150 GeneralCommandLine commandLine = new GeneralCommandLine();
151 commandLine.setExePath("cmd.exe");
152 // the test will fails if "call" is omitted
153 commandLine.addParameters("/D", "/C", "call", cmdScript.getAbsolutePath());
154 commandLine.addParameters(args);
155 String output = execAndGetOutput(commandLine, null);
156 assertParamPassingTestOutput(output, args);
160 private File createCmdFileLaunchingJavaApp() throws Exception {
161 File cmdScript = FileUtil.createTempFile(new File(PathManager.getTempPath(), "My Program Files" /* path with spaces */),
162 "my-script", ".cmd", true, true);
163 GeneralCommandLine commandLine = makeJavaCommand(ParamPassingTest.class, null);
164 FileUtil.writeToFile(cmdScript, "@" + commandLine.getCommandLineString() + " %*");
165 if (!cmdScript.setExecutable(true, true)) {
166 throw new ExecutionException("Failed to make temp file executable: " + cmdScript);
171 private static void assertParamPassingTestOutput(@NotNull String actualOutput, @NotNull String... expectedOutputParameters) {
172 String content = StringUtil.join(expectedOutputParameters, "\n");
173 if (expectedOutputParameters.length > 0) {
176 assertEquals(content, StringUtil.convertLineSeparators(actualOutput));
180 public void unicodeArguments() throws Exception {
181 assumeTrue("UTF-8".equals(System.getProperty("file.encoding")));
184 GeneralCommandLine commandLine;
185 String encoding = null;
186 if (SystemInfo.isWindows) {
187 script = ExecUtil.createTempExecutableScript(
189 "WSH.Echo(\"=====\");\n" +
190 "for (i = 0; i < WSH.Arguments.length; i++) {\n" +
191 " WSH.Echo(WSH.Arguments(i));\n" +
193 "WSH.Echo(\"=====\");\n"
195 commandLine = new GeneralCommandLine("cscript", "//Nologo", "//U", script.getPath());
196 encoding = "UTF-16LE";
199 script = ExecUtil.createTempExecutableScript(
203 "for f in \"$@\" ; do echo \"$f\"; done\n" +
206 commandLine = new GeneralCommandLine(script.getPath());
210 commandLine.addParameters("немного", "юникодных", "параметров");
211 String output = execAndGetOutput(commandLine, encoding);
212 assertEquals("=====\nнемного\nюникодных\nпараметров\n=====\n", StringUtil.convertLineSeparators(output));
215 FileUtil.delete(script);
220 public void winShellCommand() throws Exception {
221 assumeTrue(SystemInfo.isWindows);
223 String string = "http://localhost/wtf?a=b&c=d";
224 String echo = ExecUtil.execAndReadLine(new GeneralCommandLine(ExecUtil.getWindowsShellName(), "/c", "echo", string));
225 assertEquals('"' + string + '"', echo);
229 public void winShellScriptQuoting() throws Exception {
230 assumeTrue(SystemInfo.isWindows);
231 String scriptPrefix = "my_script";
232 for (String cmdScriptExt : new String[] {".cmd", ".bat"}) {
233 File script = ExecUtil.createTempExecutableScript(
234 scriptPrefix, cmdScriptExt,
237 String param = "a&b";
238 GeneralCommandLine commandLine = new GeneralCommandLine(script.getAbsolutePath(), param);
239 String text = commandLine.getPreparedCommandLine(Platform.WINDOWS);
240 assertEquals(commandLine.getExePath() + "\n" + StringUtil.wrapWithDoubleQuote(param), text);
242 String output = execAndGetOutput(commandLine, null);
243 assertEquals(StringUtil.wrapWithDoubleQuote(param), output.trim());
246 FileUtil.delete(script);
252 public void winShellQuotingWithExtraSwitch() throws Exception {
253 assumeTrue(SystemInfo.isWindows);
254 String param = "a&b";
255 GeneralCommandLine commandLine = new GeneralCommandLine("cmd", "/D", "/C", "echo", param);
256 String output = execAndGetOutput(commandLine, null);
257 assertEquals(StringUtil.wrapWithDoubleQuote(param), output.trim());
261 public void hackyEnvMap () throws Exception {
262 Map<String, String> env = new GeneralCommandLine().getEnvironment();
264 //noinspection ConstantConditions
268 env.put("key1", null);
269 fail("null values should be rejected");
271 catch (AssertionError ignored) { }
274 Map<String, String> indirect = newHashMap(pair("key2", (String)null));
275 env.putAll(indirect);
276 fail("null values should be rejected");
278 catch (AssertionError ignored) { }
282 public void environmentPassing() throws Exception {
283 Map<String, String> testEnv = new HashMap<String, String>();
284 testEnv.put("VALUE_1", "some value");
285 testEnv.put("VALUE_2", "another\n\"value\"");
287 GeneralCommandLine commandLine = makeJavaCommand(EnvPassingTest.class, null);
288 checkEnvPassing(commandLine, testEnv, true);
289 checkEnvPassing(commandLine, testEnv, false);
293 public void unicodeEnvironment() throws Exception {
294 assumeTrue("UTF-8".equals(System.getProperty("file.encoding")));
296 Map<String, String> testEnv = new HashMap<String, String>();
297 testEnv.put("VALUE_1", "немного");
298 testEnv.put("VALUE_2", "юникода");
300 GeneralCommandLine commandLine = makeJavaCommand(EnvPassingTest.class, null);
301 checkEnvPassing(commandLine, testEnv, true);
302 checkEnvPassing(commandLine, testEnv, false);
306 public void emptyEnvironmentPassing() throws Exception {
307 Map<String, String> env = newHashMap(pair("a", "b"), pair("", "c"));
308 Map<String, String> expected = newHashMap(pair("a", "b"));
309 GeneralCommandLine commandLine = makeJavaCommand(EnvPassingTest.class, null);
310 checkEnvPassing(commandLine, env, expected, false);
313 private static String execAndGetOutput(GeneralCommandLine commandLine, @Nullable String encoding) throws Exception {
314 Process process = commandLine.createProcess();
315 String stdOut = loadTextFromStream(process.getInputStream(), encoding);
316 String stdErr = loadTextFromStream(process.getErrorStream(), encoding);
317 int result = process.waitFor();
318 assertEquals("Command:\n" + commandLine.getCommandLineString()
319 + "\nStandard output:\n" + stdOut
320 + "\nStandard error:\n" + stdErr,
325 private static String loadTextFromStream(@NotNull InputStream stream, @Nullable String encoding) throws IOException {
326 byte[] bytes = FileUtil.loadBytes(stream);
327 return encoding != null ? new String(bytes, encoding) : new String(bytes);
330 private GeneralCommandLine makeJavaCommand(Class<?> testClass, @Nullable File copyTo) throws IOException, URISyntaxException {
331 String className = testClass.getName();
332 URL url = getClass().getClassLoader().getResource(className.replace(".", "/") + ".class");
335 GeneralCommandLine commandLine = new GeneralCommandLine();
336 commandLine.setExePath(System.getProperty("java.home") + (SystemInfo.isWindows ? "\\bin\\java.exe" : "/bin/java"));
338 String encoding = System.getProperty("file.encoding");
339 if (encoding != null) {
340 commandLine.addParameter("-D" + "file.encoding=" + encoding);
343 commandLine.addParameter("-cp");
344 String[] packages = className.split("\\.");
345 File classFile = new File(url.toURI());
346 if (copyTo == null) {
347 File dir = classFile;
348 for (String ignored : packages) dir = dir.getParentFile();
349 commandLine.addParameter(dir.getPath());
353 for (int i = 0; i < packages.length - 1; i++) dir = new File(dir, packages[i]);
354 FileUtil.copy(classFile, new File(dir, classFile.getName()));
355 commandLine.addParameter(copyTo.getPath());
358 commandLine.addParameter(className);
359 commandLine.setRedirectErrorStream(true);
364 private static void checkEnvPassing(GeneralCommandLine commandLine, Map<String, String> testEnv, boolean passParentEnv) throws Exception {
365 checkEnvPassing(commandLine, testEnv, testEnv, passParentEnv);
368 private static void checkEnvPassing(GeneralCommandLine commandLine,
369 Map<String, String> testEnv,
370 Map<String, String> expectedOutputEnv,
371 boolean passParentEnv) throws Exception {
372 commandLine.getEnvironment().putAll(testEnv);
373 commandLine.setPassParentEnvironment(passParentEnv);
374 String output = execAndGetOutput(commandLine, null);
376 Set<String> lines = new HashSet<String>(Arrays.asList(StringUtil.convertLineSeparators(output).split("\n")));
377 lines.remove("=====");
379 for (Map.Entry<String, String> entry : expectedOutputEnv.entrySet()) {
380 String str = EnvPassingTest.format(entry);
381 assertTrue("\"" + str + "\" should be in " + lines,
382 lines.contains(str));
385 Map<String, String> parentEnv = System.getenv();
386 List<String> missed = new ArrayList<String>();
387 for (Map.Entry<String, String> entry : parentEnv.entrySet()) {
388 String str = EnvPassingTest.format(entry);
389 if (!lines.contains(str)) {
394 long pctMissed = Math.round((100.0 * missed.size()) / parentEnv.size());
395 if (passParentEnv && pctMissed > 25 || !passParentEnv && pctMissed < 75) {
396 fail("% missed: " + pctMissed + ", missed: " + missed + ", passed: " + lines);