1 /* 2 * Copyright (c) 2007, 2010, 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 5030233 6214916 6356475 6571029 6684582 6742159 4459600 6758881 6753938 27 * 6894719 6968053 28 * @summary Argument parsing validation. 29 * @compile -XDignore.symbol.file Arrrghs.java TestHelper.java 30 * @run main Arrrghs 31 */ 32 33 import java.io.BufferedReader; 34 import java.io.File; 35 import java.io.FileNotFoundException; 36 import java.io.IOException; 37 import java.io.InputStream; 38 import java.io.InputStreamReader; 39 import java.util.Map; 40 41 public class Arrrghs { 42 private Arrrghs(){} 43 /** 44 * This class provides various tests for arguments processing. 45 * A group of tests to ensure that arguments are passed correctly to 46 * a child java process upon a re-exec, this typically happens when 47 * a version other than the one being executed is requested by the user. 48 * 49 * History: these set of tests were part of Arrrghs.sh. The MKS shell 50 * implementations were notoriously buggy. Implementing these tests purely 51 * in Java is not only portable but also robust. 52 * 53 */ 54 55 // The version string to force a re-exec 56 final static String VersionStr = "-version:1.1+"; 57 58 // The Cookie or the pattern we match in the debug output. 59 final static String Cookie = "ReExec Args: "; 60 61 /* 62 * SIGH, On Windows all strings are quoted, we need to unwrap it 63 */ 64 private static String removeExtraQuotes(String in) { 65 if (TestHelper.isWindows) { 66 // Trim the string and remove the enclosed quotes if any. 67 in = in.trim(); 68 if (in.startsWith("\"") && in.endsWith("\"")) { 69 return in.substring(1, in.length()-1); 70 } 71 } 72 return in; 73 } 74 75 /* 76 * This method detects the cookie in the output stream of the process. 77 */ 78 private static boolean detectCookie(InputStream istream, 79 String expectedArguments) throws IOException { 80 BufferedReader rd = new BufferedReader(new InputStreamReader(istream)); 81 boolean retval = false; 82 83 String in = rd.readLine(); 84 while (in != null) { 85 if (TestHelper.debug) System.out.println(in); 86 if (in.startsWith(Cookie)) { 87 String detectedArgument = removeExtraQuotes(in.substring(Cookie.length())); 88 if (expectedArguments.equals(detectedArgument)) { 89 retval = true; 90 } else { 91 System.out.println("Error: Expected Arguments\t:'" + 92 expectedArguments + "'"); 93 System.out.println(" Detected Arguments\t:'" + 94 detectedArgument + "'"); 95 } 96 // Return the value asap if not in debug mode. 97 if (!TestHelper.debug) { 98 rd.close(); 99 istream.close(); 100 return retval; 101 } 102 } 103 in = rd.readLine(); 104 } 105 return retval; 106 } 107 108 private static boolean doTest0(ProcessBuilder pb, String expectedArguments) { 109 boolean retval = false; 110 try { 111 pb.redirectErrorStream(true); 112 Process p = pb.start(); 113 retval = detectCookie(p.getInputStream(), expectedArguments); 114 p.waitFor(); 115 p.destroy(); 116 } catch (Exception ex) { 117 ex.printStackTrace(); 118 throw new RuntimeException(ex.getMessage()); 119 } 120 return retval; 121 } 122 123 /** 124 * This method return true if the expected and detected arguments are the same. 125 * Quoting could cause dissimilar testArguments and expected arguments. 126 */ 127 static int doTest(String testArguments, String expectedPattern) { 128 ProcessBuilder pb = new ProcessBuilder(TestHelper.javaCmd, 129 VersionStr, testArguments); 130 131 Map<String, String> env = pb.environment(); 132 env.put("_JAVA_LAUNCHER_DEBUG", "true"); 133 return doTest0(pb, testArguments) ? 0 : 1; 134 } 135 136 /** 137 * A convenience method for identical test pattern and expected arguments 138 */ 139 static int doTest(String testPattern) { 140 return doTest(testPattern, testPattern); 141 } 142 143 static void quoteParsingTests() { 144 /* 145 * Tests for 6214916 146 * These tests require that a JVM (any JVM) be installed in the system registry. 147 * If none is installed, skip this test. 148 */ 149 TestHelper.TestResult tr = 150 TestHelper.doExec(TestHelper.javaCmd, VersionStr, "-version"); 151 if (!tr.isOK()) { 152 System.err.println("Warning:Argument Passing Tests were skipped, " + 153 "no java found in system registry."); 154 return; 155 } 156 157 // Basic test 158 TestHelper.testExitValue += doTest("-a -b -c -d"); 159 160 // Basic test with many spaces 161 TestHelper.testExitValue += doTest("-a -b -c -d"); 162 163 // Quoted whitespace does matter ? 164 TestHelper.testExitValue += doTest("-a \"\"-b -c\"\" -d"); 165 166 167 // Escaped quotes outside of quotes as literals 168 TestHelper.testExitValue += doTest("-a \\\"-b -c\\\" -d"); 169 170 // Check for escaped quotes inside of quotes as literal 171 TestHelper.testExitValue += doTest("-a \"-b \\\"stuff\\\"\" -c -d"); 172 173 // A quote preceeded by an odd number of slashes is a literal quote 174 TestHelper.testExitValue += doTest("-a -b\\\\\\\" -c -d"); 175 176 // A quote preceeded by an even number of slashes is a literal quote 177 // see 6214916. 178 TestHelper.testExitValue += doTest("-a -b\\\\\\\\\" -c -d"); 179 180 // Make sure that whitespace doesn't interfere with the removal of the 181 // appropriate tokens. (space-tab-space preceeds -jre-restict-search). 182 TestHelper.testExitValue += doTest("-a -b \t -jre-restrict-search -c -d","-a -b -c -d"); 183 184 // Make sure that the mJRE tokens being stripped, aren't stripped if 185 // they happen to appear as arguments to the main class. 186 TestHelper.testExitValue += doTest("foo -version:1.1+"); 187 188 System.out.println("Completed arguments quoting tests with " + 189 TestHelper.testExitValue + " errors"); 190 } 191 192 /* 193 * These tests are usually run on non-existent targets to check error results 194 */ 195 static void runBasicErrorMessageTests() { 196 // Tests for 5030233 197 TestHelper.TestResult tr = TestHelper.doExec(TestHelper.javaCmd, "-cp"); 198 tr.checkNegative(); 199 tr.isNotZeroOutput(); 200 System.out.println(tr); 201 202 tr = TestHelper.doExec(TestHelper.javaCmd, "-classpath"); 203 tr.checkNegative(); 204 tr.isNotZeroOutput(); 205 System.out.println(tr); 206 207 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar"); 208 tr.checkNegative(); 209 tr.isNotZeroOutput(); 210 System.out.println(tr); 211 212 tr = TestHelper.doExec(TestHelper.javacCmd, "-cp"); 213 tr.checkNegative(); 214 tr.isNotZeroOutput(); 215 System.out.println(tr); 216 217 // Test for 6356475 "REGRESSION:"java -X" from cmdline fails" 218 tr = TestHelper.doExec(TestHelper.javaCmd, "-X"); 219 tr.checkPositive(); 220 tr.isNotZeroOutput(); 221 System.out.println(tr); 222 223 tr = TestHelper.doExec(TestHelper.javaCmd, "-help"); 224 tr.checkPositive(); 225 tr.isNotZeroOutput(); 226 System.out.println(tr); 227 228 // 6753938, test for non-negative exit value for an incorrectly formed 229 // command line, '% java' 230 tr = TestHelper.doExec(TestHelper.javaCmd); 231 tr.checkNegative(); 232 tr.isNotZeroOutput(); 233 System.out.println(tr); 234 235 // 6753938, test for non-negative exit value for an incorrectly formed 236 // command line, '% java -Xcomp' 237 tr = TestHelper.doExec(TestHelper.javaCmd, "-Xcomp"); 238 tr.checkNegative(); 239 tr.isNotZeroOutput(); 240 System.out.println(tr); 241 } 242 243 /* 244 * A set of tests which tests various dispositions of the main method. 245 */ 246 static void runMainMethodTests() throws FileNotFoundException { 247 TestHelper.TestResult tr = null; 248 249 // a missing class 250 TestHelper.createJar("MIA", new File("some.jar"), new File("Foo"), 251 (String[])null); 252 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); 253 tr.contains("Error: Could not find or load main class MIA"); 254 System.out.println(tr); 255 // use classpath to check 256 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "MIA"); 257 tr.contains("Error: Could not find or load main class MIA"); 258 System.out.println(tr); 259 260 // incorrect method access 261 TestHelper.createJar(new File("some.jar"), new File("Foo"), 262 "private static void main(String[] args){}"); 263 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); 264 tr.contains("Error: Main method not found in class Foo"); 265 System.out.println(tr); 266 // use classpath to check 267 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); 268 tr.contains("Error: Main method not found in class Foo"); 269 System.out.println(tr); 270 271 // incorrect return type 272 TestHelper.createJar(new File("some.jar"), new File("Foo"), 273 "public static int main(String[] args){return 1;}"); 274 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); 275 tr.contains("Error: Main method must return a value of type void in class Foo"); 276 System.out.println(tr); 277 // use classpath to check 278 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); 279 tr.contains("Error: Main method must return a value of type void in class Foo"); 280 System.out.println(tr); 281 282 // incorrect parameter type 283 TestHelper.createJar(new File("some.jar"), new File("Foo"), 284 "public static void main(Object[] args){}"); 285 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); 286 tr.contains("Error: Main method not found in class Foo"); 287 System.out.println(tr); 288 // use classpath to check 289 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); 290 tr.contains("Error: Main method not found in class Foo"); 291 System.out.println(tr); 292 293 // incorrect method type - non-static 294 TestHelper.createJar(new File("some.jar"), new File("Foo"), 295 "public void main(String[] args){}"); 296 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); 297 tr.contains("Error: Main method is not static in class Foo"); 298 System.out.println(tr); 299 // use classpath to check 300 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); 301 tr.contains("Error: Main method is not static in class Foo"); 302 System.out.println(tr); 303 304 // amongst a potpourri of kindred main methods, is the right one chosen ? 305 TestHelper.createJar(new File("some.jar"), new File("Foo"), 306 "void main(Object[] args){}", 307 "int main(Float[] args){return 1;}", 308 "private void main() {}", 309 "private static void main(int x) {}", 310 "public int main(int argc, String[] argv) {return 1;}", 311 "public static void main(String[] args) {System.out.println(\"THE_CHOSEN_ONE\");}"); 312 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); 313 tr.contains("THE_CHOSEN_ONE"); 314 System.out.println(tr); 315 // use classpath to check 316 tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo"); 317 tr.contains("THE_CHOSEN_ONE"); 318 System.out.println(tr); 319 320 // test for extraneous whitespace in the Main-Class attribute 321 TestHelper.createJar(" Foo ", new File("some.jar"), new File("Foo"), 322 "public static void main(String... args){}"); 323 tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar"); 324 tr.checkPositive(); 325 System.out.println(tr); 326 } 327 // tests 6968053, ie. we turn on the -Xdiag (for now) flag and check if 328 // the suppressed stack traces are exposed. 329 static void runDiagOptionTests() throws FileNotFoundException { 330 TestHelper.TestResult tr = null; 331 // a missing class 332 TestHelper.createJar("MIA", new File("some.jar"), new File("Foo"), 333 (String[])null); 334 tr = TestHelper.doExec(TestHelper.javaCmd, "-Xdiag", "-jar", "some.jar"); 335 tr.contains("Error: Could not find or load main class MIA"); 336 tr.contains("java.lang.ClassNotFoundException: MIA"); 337 System.out.println(tr); 338 339 // use classpath to check 340 tr = TestHelper.doExec(TestHelper.javaCmd, "-Xdiag", "-cp", "some.jar", "MIA"); 341 tr.contains("Error: Could not find or load main class MIA"); 342 tr.contains("java.lang.ClassNotFoundException: MIA"); 343 System.out.println(tr); 344 345 // a missing class on the classpath 346 tr = TestHelper.doExec(TestHelper.javaCmd, "-Xdiag", "NonExistentClass"); 347 tr.contains("Error: Could not find or load main class NonExistentClass"); 348 tr.contains("java.lang.ClassNotFoundException: NonExistentClass"); 349 System.out.println(tr); 350 } 351 352 static void test6894719() { 353 // test both arguments to ensure they exist 354 TestHelper.TestResult tr = null; 355 tr = TestHelper.doExec(TestHelper.javaCmd, 356 "-no-jre-restrict-search", "-version"); 357 tr.checkPositive(); 358 System.out.println(tr); 359 360 tr = TestHelper.doExec(TestHelper.javaCmd, 361 "-jre-restrict-search", "-version"); 362 tr.checkPositive(); 363 System.out.println(tr); 364 } 365 /** 366 * @param args the command line arguments 367 * @throws java.io.FileNotFoundException 368 */ 369 public static void main(String[] args) throws FileNotFoundException { 370 if (TestHelper.debug) { 371 System.out.println("Starting Arrrghs tests"); 372 } 373 quoteParsingTests(); 374 runBasicErrorMessageTests(); 375 runMainMethodTests(); 376 test6894719(); 377 runDiagOptionTests(); 378 if (TestHelper.testExitValue > 0) { 379 System.out.println("Total of " + TestHelper.testExitValue + " failed"); 380 System.exit(1); 381 } else { 382 System.out.println("All tests pass"); 383 } 384 } 385 }