19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
20 * CA 95054 USA or visit www.sun.com if you need additional information or |
20 * CA 95054 USA or visit www.sun.com if you need additional information or |
21 * have any questions. |
21 * have any questions. |
22 */ |
22 */ |
23 |
23 |
|
24 /** |
|
25 * @test |
|
26 * @compile -XDignore.symbol.file Arrrghs.java TestHelper.java |
|
27 * @bug 5030233 6214916 6356475 6571029 6684582 |
|
28 * @run main Arrrghs |
|
29 * @summary Argument parsing validation. |
|
30 */ |
|
31 |
24 import java.io.BufferedReader; |
32 import java.io.BufferedReader; |
25 import java.io.File; |
33 import java.io.File; |
|
34 import java.io.FileNotFoundException; |
26 import java.io.IOException; |
35 import java.io.IOException; |
27 import java.io.InputStream; |
36 import java.io.InputStream; |
28 import java.io.InputStreamReader; |
37 import java.io.InputStreamReader; |
29 import java.util.ArrayList; |
|
30 import java.util.Collection; |
|
31 import java.util.Collections; |
|
32 import java.util.List; |
|
33 import java.util.Map; |
38 import java.util.Map; |
34 import java.util.StringTokenizer; |
|
35 |
39 |
36 public class Arrrghs { |
40 public class Arrrghs { |
37 |
41 private Arrrghs(){} |
38 /** |
42 /** |
|
43 * This class provides various tests for arguments processing. |
39 * A group of tests to ensure that arguments are passed correctly to |
44 * A group of tests to ensure that arguments are passed correctly to |
40 * a child java process upon a re-exec, this typically happens when |
45 * a child java process upon a re-exec, this typically happens when |
41 * a version other than the one being executed is requested by the user. |
46 * a version other than the one being executed is requested by the user. |
42 * |
47 * |
43 * History: these set of tests were part of Arrrghs.sh. The MKS shell |
48 * History: these set of tests were part of Arrrghs.sh. The MKS shell |
44 * implementations are notoriously buggy. Implementing these tests purely |
49 * implementations were notoriously buggy. Implementing these tests purely |
45 * in Java is not only portable but also robust. |
50 * in Java is not only portable but also robust. |
46 * |
51 * |
47 */ |
52 */ |
48 |
53 |
49 /* Do not instantiate */ |
|
50 private Arrrghs() {} |
|
51 |
|
52 static String javaCmd; |
|
53 |
|
54 // The version string to force a re-exec |
54 // The version string to force a re-exec |
55 final static String VersionStr = "-version:1.1+"; |
55 final static String VersionStr = "-version:1.1+"; |
56 |
56 |
57 // The Cookie or the pattern we match in the debug output. |
57 // The Cookie or the pattern we match in the debug output. |
58 final static String Cookie = "ReExec Args: "; |
58 final static String Cookie = "ReExec Args: "; |
59 |
59 |
60 private static boolean _debug = Boolean.getBoolean("Arrrghs.Debug"); |
|
61 private static boolean isWindows = System.getProperty("os.name", "unknown").startsWith("Windows"); |
|
62 private static int exitValue = 0; |
|
63 |
|
64 private static void doUsage(String message) { |
|
65 if (message != null) System.out.println("Error: " + message); |
|
66 System.out.println("Usage: Arrrghs path_to_java"); |
|
67 System.exit(1); |
|
68 } |
|
69 |
|
70 /* |
60 /* |
71 * SIGH, On Windows all strings are quoted, we need to unwrap it |
61 * SIGH, On Windows all strings are quoted, we need to unwrap it |
72 */ |
62 */ |
73 private static String removeExtraQuotes(String in) { |
63 private static String removeExtraQuotes(String in) { |
74 if (isWindows) { |
64 if (TestHelper.isWindows) { |
75 // Trim the string and remove the enclosed quotes if any. |
65 // Trim the string and remove the enclosed quotes if any. |
76 in = in.trim(); |
66 in = in.trim(); |
77 if (in.startsWith("\"") && in.endsWith("\"")) { |
67 if (in.startsWith("\"") && in.endsWith("\"")) { |
78 return in.substring(1, in.length()-1); |
68 return in.substring(1, in.length()-1); |
79 } |
69 } |
80 } |
70 } |
81 return in; |
71 return in; |
82 } |
72 } |
83 |
73 |
84 |
|
85 /* |
74 /* |
86 * This method detects the cookie in the output stream of the process. |
75 * This method detects the cookie in the output stream of the process. |
87 */ |
76 */ |
88 private static boolean detectCookie(InputStream istream, String expectedArguments) throws IOException { |
77 private static boolean detectCookie(InputStream istream, |
|
78 String expectedArguments) throws IOException { |
89 BufferedReader rd = new BufferedReader(new InputStreamReader(istream)); |
79 BufferedReader rd = new BufferedReader(new InputStreamReader(istream)); |
90 boolean retval = false; |
80 boolean retval = false; |
91 |
81 |
92 String in = rd.readLine(); |
82 String in = rd.readLine(); |
93 while (in != null) { |
83 while (in != null) { |
94 if (_debug) System.out.println(in); |
84 if (TestHelper.debug) System.out.println(in); |
95 if (in.startsWith(Cookie)) { |
85 if (in.startsWith(Cookie)) { |
96 String detectedArgument = removeExtraQuotes(in.substring(Cookie.length())); |
86 String detectedArgument = removeExtraQuotes(in.substring(Cookie.length())); |
97 if (expectedArguments.equals(detectedArgument)) { |
87 if (expectedArguments.equals(detectedArgument)) { |
98 retval = true; |
88 retval = true; |
99 } else { |
89 } else { |
100 System.out.println("Error: Expected Arguments\t:'" + expectedArguments + "'"); |
90 System.out.println("Error: Expected Arguments\t:'" + |
101 System.out.println(" Detected Arguments\t:'" + detectedArgument + "'"); |
91 expectedArguments + "'"); |
|
92 System.out.println(" Detected Arguments\t:'" + |
|
93 detectedArgument + "'"); |
102 } |
94 } |
103 // Return the value asap if not in debug mode. |
95 // Return the value asap if not in debug mode. |
104 if (!_debug) { |
96 if (!TestHelper.debug) { |
105 rd.close(); |
97 rd.close(); |
106 istream.close(); |
98 istream.close(); |
107 return retval; |
99 return retval; |
108 } |
100 } |
109 } |
101 } |
110 in = rd.readLine(); |
102 in = rd.readLine(); |
111 } |
103 } |
112 return retval; |
104 return retval; |
113 } |
105 } |
114 |
106 |
115 private static boolean doExec0(ProcessBuilder pb, String expectedArguments) { |
107 private static boolean doTest0(ProcessBuilder pb, String expectedArguments) { |
116 boolean retval = false; |
108 boolean retval = false; |
117 try { |
109 try { |
118 pb.redirectErrorStream(true); |
110 pb.redirectErrorStream(true); |
119 Process p = pb.start(); |
111 Process p = pb.start(); |
120 retval = detectCookie(p.getInputStream(), expectedArguments); |
112 retval = detectCookie(p.getInputStream(), expectedArguments); |
129 |
121 |
130 /** |
122 /** |
131 * This method return true if the expected and detected arguments are the same. |
123 * This method return true if the expected and detected arguments are the same. |
132 * Quoting could cause dissimilar testArguments and expected arguments. |
124 * Quoting could cause dissimilar testArguments and expected arguments. |
133 */ |
125 */ |
134 static boolean doExec(String testArguments, String expectedPattern) { |
126 static int doTest(String testArguments, String expectedPattern) { |
135 ProcessBuilder pb = new ProcessBuilder(javaCmd, VersionStr, testArguments); |
127 ProcessBuilder pb = new ProcessBuilder(TestHelper.javaCmd, |
|
128 VersionStr, testArguments); |
136 |
129 |
137 Map<String, String> env = pb.environment(); |
130 Map<String, String> env = pb.environment(); |
138 env.put("_JAVA_LAUNCHER_DEBUG", "true"); |
131 env.put("_JAVA_LAUNCHER_DEBUG", "true"); |
139 return doExec0(pb, testArguments); |
132 return doTest0(pb, testArguments) ? 0 : 1; |
140 } |
133 } |
141 |
134 |
142 /** |
135 /** |
143 * A convenience method for identical test pattern and expected arguments |
136 * A convenience method for identical test pattern and expected arguments |
144 */ |
137 */ |
145 static boolean doExec(String testPattern) { |
138 static int doTest(String testPattern) { |
146 return doExec(testPattern, testPattern); |
139 return doTest(testPattern, testPattern); |
|
140 } |
|
141 |
|
142 static void quoteParsingTests() { |
|
143 /* |
|
144 * Tests for 6214916 |
|
145 * These tests require that a JVM (any JVM) be installed in the system registry. |
|
146 * If none is installed, skip this test. |
|
147 */ |
|
148 TestHelper.TestResult tr = |
|
149 TestHelper.doExec(TestHelper.javaCmd, VersionStr, "-version"); |
|
150 if (!tr.isOK()) { |
|
151 System.err.println("Warning:Argument Passing Tests were skipped, " + |
|
152 "no java found in system registry."); |
|
153 return; |
|
154 } |
|
155 |
|
156 // Basic test |
|
157 TestHelper.testExitValue += doTest("-a -b -c -d"); |
|
158 |
|
159 // Basic test with many spaces |
|
160 TestHelper.testExitValue += doTest("-a -b -c -d"); |
|
161 |
|
162 // Quoted whitespace does matter ? |
|
163 TestHelper.testExitValue += doTest("-a \"\"-b -c\"\" -d"); |
|
164 |
|
165 |
|
166 // Escaped quotes outside of quotes as literals |
|
167 TestHelper.testExitValue += doTest("-a \\\"-b -c\\\" -d"); |
|
168 |
|
169 // Check for escaped quotes inside of quotes as literal |
|
170 TestHelper.testExitValue += doTest("-a \"-b \\\"stuff\\\"\" -c -d"); |
|
171 |
|
172 // A quote preceeded by an odd number of slashes is a literal quote |
|
173 TestHelper.testExitValue += doTest("-a -b\\\\\\\" -c -d"); |
|
174 |
|
175 // A quote preceeded by an even number of slashes is a literal quote |
|
176 // see 6214916. |
|
177 TestHelper.testExitValue += doTest("-a -b\\\\\\\\\" -c -d"); |
|
178 |
|
179 // Make sure that whitespace doesn't interfere with the removal of the |
|
180 // appropriate tokens. (space-tab-space preceeds -jre-restict-search). |
|
181 TestHelper.testExitValue += doTest("-a -b \t -jre-restrict-search -c -d","-a -b -c -d"); |
|
182 |
|
183 // Make sure that the mJRE tokens being stripped, aren't stripped if |
|
184 // they happen to appear as arguments to the main class. |
|
185 TestHelper.testExitValue += doTest("foo -version:1.1+"); |
|
186 |
|
187 System.out.println("Completed arguments quoting tests with " + |
|
188 TestHelper.testExitValue + " errors"); |
|
189 } |
|
190 |
|
191 /* |
|
192 * These tests are usually run on non-existent targets to check error results |
|
193 */ |
|
194 static void runBasicErrorMessageTests() { |
|
195 // Tests for 5030233 |
|
196 TestHelper.TestResult tr = TestHelper.doExec(TestHelper.javaCmd, "-cp"); |
|
197 tr.checkNegative(); |
|
198 tr.isNotZeroOutput(); |
|
199 System.out.println(tr); |
|
200 |
|
201 tr = TestHelper.doExec(TestHelper.javaCmd, "-classpath"); |
|
202 tr.checkNegative(); |
|
203 tr.isNotZeroOutput(); |
|
204 System.out.println(tr); |
|
205 |
|
206 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar"); |
|
207 tr.checkNegative(); |
|
208 tr.isNotZeroOutput(); |
|
209 System.out.println(tr); |
|
210 |
|
211 tr = TestHelper.doExec(TestHelper.javacCmd, "-cp"); |
|
212 tr.checkNegative(); |
|
213 tr.isNotZeroOutput(); |
|
214 System.out.println(tr); |
|
215 |
|
216 // Test for 6356475 "REGRESSION:"java -X" from cmdline fails" |
|
217 tr = TestHelper.doExec(TestHelper.javaCmd, "-X"); |
|
218 tr.checkPositive(); |
|
219 tr.isNotZeroOutput(); |
|
220 System.out.println(tr); |
|
221 |
|
222 tr = TestHelper.doExec(TestHelper.javaCmd, "-help"); |
|
223 tr.checkPositive(); |
|
224 tr.isNotZeroOutput(); |
|
225 System.out.println(tr); |
|
226 } |
|
227 |
|
228 /* |
|
229 * A set of tests which tests various dispositions of the main method. |
|
230 */ |
|
231 static void runMainMethodTests() throws FileNotFoundException { |
|
232 TestHelper.TestResult tr = null; |
|
233 |
|
234 // a missing class |
|
235 TestHelper.createJar(new File("some.jar"), new File("Foo"), (String[])null); |
|
236 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); |
|
237 tr.contains("MIA"); |
|
238 System.out.println(tr); |
|
239 // use classpath to check |
|
240 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "MIA"); |
|
241 tr.contains("Error: Could not find main class MIA"); |
|
242 System.out.println(tr); |
|
243 |
|
244 // incorrect method access |
|
245 TestHelper.createJar(new File("some.jar"), new File("Foo"), |
|
246 "private static void main(String[] args){}"); |
|
247 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); |
|
248 tr.contains("Error: Main method not found in class Foo"); |
|
249 System.out.println(tr); |
|
250 // use classpath to check |
|
251 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); |
|
252 tr.contains("Error: Main method not found in class Foo"); |
|
253 System.out.println(tr); |
|
254 |
|
255 // incorrect return type |
|
256 TestHelper.createJar(new File("some.jar"), new File("Foo"), |
|
257 "public static int main(String[] args){return 1;}"); |
|
258 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); |
|
259 tr.contains("Error: Main method must return a value of type void in class Foo"); |
|
260 System.out.println(tr); |
|
261 // use classpath to check |
|
262 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); |
|
263 tr.contains("Error: Main method must return a value of type void in class Foo"); |
|
264 System.out.println(tr); |
|
265 |
|
266 // incorrect parameter type |
|
267 TestHelper.createJar(new File("some.jar"), new File("Foo"), |
|
268 "public static void main(Object[] args){}"); |
|
269 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); |
|
270 tr.contains("Error: Main method not found in class Foo"); |
|
271 System.out.println(tr); |
|
272 // use classpath to check |
|
273 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); |
|
274 tr.contains("Error: Main method not found in class Foo"); |
|
275 System.out.println(tr); |
|
276 |
|
277 // incorrect method type - non-static |
|
278 TestHelper.createJar(new File("some.jar"), new File("Foo"), |
|
279 "public void main(Object[] args){}"); |
|
280 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); |
|
281 tr.contains("Error: Main method not found in class Foo"); |
|
282 System.out.println(tr); |
|
283 // use classpath to check |
|
284 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); |
|
285 tr.contains("Error: Main method not found in class Foo"); |
|
286 System.out.println(tr); |
|
287 |
|
288 // amongst a potpourri of kindred main methods, is the right one chosen ? |
|
289 TestHelper.createJar(new File("some.jar"), new File("Foo"), |
|
290 "void main(Object[] args){}", |
|
291 "int main(Float[] args){return 1;}", |
|
292 "private void main() {}", |
|
293 "private static void main(int x) {}", |
|
294 "public int main(int argc, String[] argv) {return 1;}", |
|
295 "public static void main(String[] args) {System.out.println(\"THE_CHOSEN_ONE\");}"); |
|
296 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); |
|
297 tr.contains("THE_CHOSEN_ONE"); |
|
298 System.out.println(tr); |
|
299 // use classpath to check |
|
300 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); |
|
301 tr.contains("THE_CHOSEN_ONE"); |
|
302 System.out.println(tr); |
147 } |
303 } |
148 |
304 |
149 /** |
305 /** |
150 * @param args the command line arguments |
306 * @param args the command line arguments |
151 */ |
307 * @throws java.io.FileNotFoundException |
152 public static void main(String[] args) { |
308 */ |
153 if (args.length < 1 && args[0] == null) { |
309 public static void main(String[] args) throws FileNotFoundException { |
154 doUsage("Invalid number of arguments"); |
310 if (TestHelper.debug) System.out.println("Starting Arrrghs tests"); |
155 } |
311 quoteParsingTests(); |
156 |
312 runBasicErrorMessageTests(); |
157 javaCmd = args[0]; |
313 runMainMethodTests(); |
158 |
314 if (TestHelper.testExitValue > 0) { |
159 if (!new File(javaCmd).canExecute()) { |
315 System.out.println("Total of " + TestHelper.testExitValue + " failed"); |
160 if (isWindows && new File(javaCmd + ".exe").canExecute()) { |
316 System.exit(1); |
161 javaCmd = javaCmd + ".exe"; |
317 } else { |
162 } else { |
318 System.out.println("All tests pass"); |
163 doUsage("The java executable must exist"); |
319 } |
164 } |
320 } |
165 } |
|
166 |
|
167 if (_debug) System.out.println("Starting Arrrghs tests"); |
|
168 // Basic test |
|
169 if (!doExec("-a -b -c -d")) exitValue++; |
|
170 |
|
171 // Basic test with many spaces |
|
172 if (!doExec("-a -b -c -d")) exitValue++; |
|
173 |
|
174 // Quoted whitespace does matter ? |
|
175 if (!doExec("-a \"\"-b -c\"\" -d")) exitValue++; |
|
176 |
|
177 // Escaped quotes outside of quotes as literals |
|
178 if (!doExec("-a \\\"-b -c\\\" -d")) exitValue++; |
|
179 |
|
180 // Check for escaped quotes inside of quotes as literal |
|
181 if (!doExec("-a \"-b \\\"stuff\\\"\" -c -d")) exitValue++; |
|
182 |
|
183 // A quote preceeded by an odd number of slashes is a literal quote |
|
184 if (!doExec("-a -b\\\\\\\" -c -d")) exitValue++; |
|
185 |
|
186 // A quote preceeded by an even number of slashes is a literal quote |
|
187 // see 6214916. |
|
188 if (!doExec("-a -b\\\\\\\\\" -c -d")) exitValue++; |
|
189 |
|
190 // Make sure that whitespace doesn't interfere with the removal of the |
|
191 // appropriate tokens. (space-tab-space preceeds -jre-restict-search). |
|
192 if (!doExec("-a -b \t -jre-restrict-search -c -d","-a -b -c -d")) exitValue++; |
|
193 |
|
194 // Make sure that the mJRE tokens being stripped, aren't stripped if |
|
195 // they happen to appear as arguments to the main class. |
|
196 if (!doExec("foo -version:1.1+")) exitValue++; |
|
197 |
|
198 System.out.println("Completed Arrrghs arguments quoting/matching tests with " + exitValue + " errors"); |
|
199 System.exit(exitValue); |
|
200 } |
|
201 |
|
202 } |
321 } |