1 /*
2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /**
25  * @test
26  * @bug 8027634 8231863
27  * @summary Argument parsing from file
28  * @modules jdk.compiler
29  *          jdk.zipfs
30  * @build TestHelper
31  * @run main ArgsFileTest
32  */
33 import java.io.File;
34 import java.io.IOException;
35 import java.util.ArrayList;
36 import java.util.Arrays;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.regex.Matcher;
42 import java.util.regex.Pattern;
43 
44 public class ArgsFileTest extends TestHelper {
45     private static File testJar = null;
46     private static Map<String, String> env = new HashMap<>();
47 
init()48     static void init() throws IOException {
49         if  (testJar != null) {
50             return;
51         }
52         testJar = new File("test.jar");
53         StringBuilder tsrc = new StringBuilder();
54         tsrc.append("public static void main(String... args) {\n");
55         tsrc.append("   for (String x : args) {\n");
56         tsrc.append("        System.out.println(x);\n");
57         tsrc.append("   }\n");
58         tsrc.append("}\n");
59         createJar(testJar, new File("Foo"), tsrc.toString());
60 
61         env.put(JLDEBUG_KEY, "true");
62     }
63 
createArgFile(String fname, List<String> lines, boolean endWithNewline)64     private File createArgFile(String fname, List<String> lines, boolean endWithNewline) throws IOException {
65         File argFile = new File(fname);
66         argFile.delete();
67         createAFile(argFile, lines, endWithNewline);
68         return argFile;
69     }
70 
createArgFile(String fname, List<String> lines)71     private File createArgFile(String fname, List<String> lines) throws IOException {
72         return createArgFile(fname, lines, true);
73     }
74 
verifyOptions(List<String> args, TestResult tr)75     private void verifyOptions(List<String> args, TestResult tr) {
76         if (args.isEmpty()) {
77             return;
78         }
79 
80         int i = 1;
81         for (String x : args) {
82             tr.matches(".*argv\\[" + i + "\\] = " + Pattern.quote(x) + ".*");
83             i++;
84         }
85         if (! tr.testStatus) {
86             System.out.println(tr);
87             throw new RuntimeException("test fails");
88         }
89     }
90 
verifyUserArgs(List<String> args, TestResult tr, int index)91     private void verifyUserArgs(List<String> args, TestResult tr, int index) {
92         if (javaCmd != TestHelper.javaCmd) {
93             tr.contains("\tFirst application arg index: 1");
94         } else {
95             tr.contains("\tFirst application arg index: " + index);
96 
97             for (String arg: args) {
98                 tr.matches("^" + Pattern.quote(arg) + "$");
99             }
100         }
101 
102         if (! tr.testStatus) {
103             System.out.println(tr);
104             throw new RuntimeException("test fails");
105         }
106     }
107 
108     @Test
expandAll()109     public void expandAll() throws IOException {
110         List<String> lines = new ArrayList<>();
111         lines.add("-Xmx32m");
112         lines.add("-Xint");
113         File argFile1 = createArgFile("argFile1", lines);
114         lines = new ArrayList<>();
115         lines.add("-jar");
116         lines.add("test.jar");
117         lines.add("uarg1 @uarg2 @@uarg3 -uarg4 uarg5");
118         File argFile2 = createArgFile("argFile2", lines);
119 
120         TestResult tr = doExec(env, javaCmd, "@argFile1", "@argFile2");
121 
122         List<String> appArgs = new ArrayList<>();
123         appArgs.add("uarg1");
124         appArgs.add("@uarg2");
125         appArgs.add("@@uarg3");
126         appArgs.add("-uarg4");
127         appArgs.add("uarg5");
128 
129         List<String> options = new ArrayList<>();
130         options.add("-Xmx32m");
131         options.add("-Xint");
132         options.add("-jar");
133         options.add("test.jar");
134         options.addAll(appArgs);
135 
136         verifyOptions(options, tr);
137         verifyUserArgs(appArgs, tr, 5);
138         argFile1.delete();
139         argFile2.delete();
140 
141         File cpFile = createArgFile("cpFile", Arrays.asList("-cp", "test.jar"));
142         List<String> appCmd = new ArrayList<>();
143         appCmd.add("Foo");
144         appCmd.addAll(appArgs);
145         File appFile = createArgFile("appFile", appCmd);
146 
147         tr = doExec(env, javaCmd, "@cpFile", "@appFile");
148         verifyOptions(Arrays.asList("-cp", "test.jar", "Foo",
149                 "uarg1", "@uarg2", "@@uarg3", "-uarg4", "uarg5"), tr);
150         verifyUserArgs(appArgs, tr, 4);
151         cpFile.delete();
152         appFile.delete();
153     }
154 
155     @Test
escapeArg()156     public void escapeArg() throws IOException {
157         List<String> lines = new ArrayList<>();
158         lines.add("-Xmx32m");
159         lines.add("-Xint");
160         File argFile1 = createArgFile("argFile1", lines);
161 
162         TestResult tr = doExec(env, javaCmd, "-cp", "@@arg", "-cp", "@",
163                 "-cp", "@@@cp", "@argFile1", "@@@@Main@@@@", "-version");
164         List<String> options = new ArrayList<>();
165         options.add("-cp");
166         options.add("@arg");
167         options.add("-cp");
168         options.add("@");
169         options.add("-cp");
170         options.add("@@cp");
171         options.add("-Xmx32m");
172         options.add("-Xint");
173         options.add("@@@Main@@@@");
174         options.add("-version");
175         verifyOptions(options, tr);
176         verifyUserArgs(Collections.emptyList(), tr, options.size());
177         argFile1.delete();
178     }
179 
180     @Test
killSwitch()181     public void killSwitch() throws IOException {
182         List<String> lines = new ArrayList<>();
183         lines.add("-Xmx32m");
184         lines.add("-Xint");
185         File argFile1 = createArgFile("argFile1", lines);
186         lines = new ArrayList<>();
187         lines.add("-jar");
188         lines.add("test.jar");
189         lines.add("uarg1 @uarg2 @@uarg3 -uarg4 uarg5");
190         File argFile2 = createArgFile("argFile2", lines);
191         File argKill = createArgFile("argKill",
192             Collections.singletonList("--disable-@files"));
193 
194         TestResult tr = doExec(env, javaCmd, "@argFile1", "--disable-@files", "@argFile2");
195         List<String> options = new ArrayList<>();
196         options.add("-Xmx32m");
197         options.add("-Xint");
198         options.add("--disable-@files");
199         options.add("@argFile2");
200         verifyOptions(options, tr);
201         // Main class is @argFile2
202         verifyUserArgs(Collections.emptyList(), tr, 5);
203 
204         // Specify in file is same as specify inline
205         tr = doExec(env, javaCmd, "@argFile1", "@argKill", "@argFile2");
206         verifyOptions(options, tr);
207         // Main class is @argFile2
208         verifyUserArgs(Collections.emptyList(), tr, 5);
209 
210         // multiple is fine, once on is on.
211         tr = doExec(env, javaCmd, "@argKill", "@argFile1", "--disable-@files", "@argFile2");
212         options = Arrays.asList("--disable-@files", "@argFile1",
213                 "--disable-@files", "@argFile2");
214         verifyOptions(options, tr);
215         verifyUserArgs(Collections.emptyList(), tr, 3);
216 
217         // after main class, becoming an user application argument
218         tr = doExec(env, javaCmd, "@argFile2", "@argKill");
219         options = Arrays.asList("-jar", "test.jar", "uarg1", "@uarg2", "@@uarg3",
220                 "-uarg4", "uarg5", "@argKill");
221         verifyOptions(options, tr);
222         verifyUserArgs(Arrays.asList("uarg1", "@uarg2", "@@uarg3",
223                 "-uarg4", "uarg5", "@argKill"), tr, 3);
224 
225         argFile1.delete();
226         argFile2.delete();
227         argKill.delete();
228     }
229 
230     @Test
userApplication()231     public void userApplication() throws IOException {
232         List<String> lines = new ArrayList<>();
233         lines.add("-Xmx32m");
234         lines.add("-Xint");
235         File vmArgs = createArgFile("vmArgs", lines);
236         File jarOpt = createArgFile("jarOpt", Arrays.asList("-jar"));
237         File cpOpt = createArgFile("cpOpt", Arrays.asList("-cp"));
238         File jarArg = createArgFile("jarArg", Arrays.asList("test.jar"));
239         File userArgs = createArgFile("userArgs", Arrays.asList("-opt", "arg", "--longopt"));
240 
241         TestResult tr = doExec(env, javaCmd,
242                 "@vmArgs", "@jarOpt", "test.jar", "-opt", "arg", "--longopt");
243         verifyOptions(Arrays.asList(
244                 "-Xmx32m", "-Xint", "-jar", "test.jar", "-opt", "arg", "--longopt"), tr);
245         verifyUserArgs(Arrays.asList("-opt", "arg", "--longopt"), tr, 5);
246 
247         tr = doExec(env, javaCmd, "@jarOpt", "@jarArg", "@vmArgs");
248         verifyOptions(Arrays.asList("-jar", "test.jar", "@vmArgs"), tr);
249         verifyUserArgs(Arrays.asList("@vmArgs"), tr, 3);
250 
251         tr = doExec(env, javaCmd, "-cp", "@jarArg", "@vmArgs", "Foo", "@userArgs");
252         verifyOptions(Arrays.asList("-cp", "test.jar", "-Xmx32m", "-Xint",
253                 "Foo", "@userArgs"), tr);
254         verifyUserArgs(Arrays.asList("@userArgs"), tr, 6);
255 
256         tr = doExec(env, javaCmd, "@cpOpt", "@jarArg", "@vmArgs", "Foo", "@userArgs");
257         verifyOptions(Arrays.asList("-cp", "test.jar", "-Xmx32m", "-Xint",
258                 "Foo", "@userArgs"), tr);
259         verifyUserArgs(Arrays.asList("@userArgs"), tr, 6);
260 
261         tr = doExec(env, javaCmd, "@cpOpt", "test.jar", "@vmArgs", "Foo", "@userArgs");
262         verifyOptions(Arrays.asList("-cp", "test.jar", "-Xmx32m", "-Xint",
263                 "Foo", "@userArgs"), tr);
264         verifyUserArgs(Arrays.asList("@userArgs"), tr, 6);
265 
266         vmArgs.delete();
267         jarOpt.delete();
268         cpOpt.delete();
269         jarArg.delete();
270         userArgs.delete();
271     }
272 
273     @Test
userApplicationWithoutEmptyLastLine()274     public void userApplicationWithoutEmptyLastLine() throws IOException {
275         File cpOpt = createArgFile("cpOpt", Arrays.asList("-classpath ."), false);
276         File vmArgs = createArgFile("vmArgs", Arrays.asList("-Xint"), false);
277 
278         TestResult tr = doExec(env, javaCmd, "-cp", "test.jar", "@cpOpt", "Foo", "-test");
279         verifyOptions(Arrays.asList("-cp", "test.jar", "-classpath", ".", "Foo", "-test"), tr);
280         verifyUserArgs(Arrays.asList("-test"), tr, 6);
281 
282         tr = doExec(env, javaCmd,  "-cp", "test.jar", "@vmArgs", "Foo", "-test");
283         verifyOptions(Arrays.asList("-cp", "test.jar", "-Xint", "Foo", "-test"), tr);
284         verifyUserArgs(Arrays.asList("-test"), tr, 5);
285 
286         cpOpt.delete();
287         vmArgs.delete();
288     }
289 
290     // test with missing file
291     @Test
missingFileNegativeTest()292     public void missingFileNegativeTest() throws IOException {
293         TestResult tr = doExec(javaCmd, "@" + "missing.cmd");
294         tr.checkNegative();
295         tr.contains("Error: could not open `missing.cmd'");
296         if (!tr.testStatus) {
297             System.out.println(tr);
298             throw new RuntimeException("test fails");
299         }
300     }
301 
main(String... args)302     public static void main(String... args) throws Exception {
303         init();
304         ArgsFileTest a = new ArgsFileTest();
305         a.run(args);
306         if (testExitValue > 0) {
307             System.out.println("Total of " + testExitValue + " failed");
308             System.exit(1);
309         } else {
310             System.out.println("All tests pass");
311         }
312     }
313 }
314