1 /* 2 * Copyright (c) 2012, 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 8001533 8004547 8035782 27 * @summary Test launching FX application with java -jar 28 * Test uses main method and blank main method, a jfx app class and an incorrest 29 * jfx app class, a main-class for the manifest, a bogus one and none. 30 * All should execute except the incorrect fx app class entries. 31 * @modules java.compiler 32 * @run main/othervm FXLauncherTest 33 * @key intermittent 34 */ 35 import java.io.File; 36 import java.io.IOException; 37 import java.util.ArrayList; 38 import java.util.List; 39 40 public class FXLauncherTest extends TestHelper { 41 private static final String FX_MARKER_CLASS = "javafx.application.Application"; 42 private static void line() { 43 System.out.println("_____________________________________________"); 44 } 45 private static File MainJavaFile = null; 46 private static final File FXtestJar = new File("fxtest.jar"); 47 private static final File ManifestFile = new File("manifest.txt"); 48 private static final File ScratchDir = new File("."); 49 50 /* standard main class can be used as java main for fx app class */ 51 static final String StdMainClass = "helloworld.HelloWorld"; 52 static final String ExtMainClass = "helloworld.ExtHello"; 53 static final String NonFXMainClass = "helloworld.HelloJava"; 54 static int testcount = 0; 55 56 /* a main method and a blank. */ 57 static final String[] MAIN_METHODS = { 58 "public static void main(String[] args) { launch(args); }", 59 " " 60 }; 61 62 // Array of parameters to pass to fx application. 63 static final String[] APP_PARMS = { "one", "two" }; 64 65 // Create fx java file for test application 66 static void createJavaFile(String mainmethod) { 67 try { 68 String mainClass = "HelloWorld"; 69 List<String> contents = new ArrayList<>(); 70 contents.add("package helloworld;"); 71 contents.add("import javafx.application.Application;"); 72 contents.add("import javafx.event.ActionEvent;"); 73 contents.add("import javafx.event.EventHandler;"); 74 contents.add("import javafx.scene.Scene;"); 75 contents.add("import javafx.scene.control.Button;"); 76 contents.add("import javafx.scene.layout.StackPane;"); 77 contents.add("import javafx.stage.Stage;"); 78 contents.add("public class HelloWorld extends Application {"); 79 contents.add(mainmethod); 80 contents.add("@Override"); 81 contents.add("public void start(Stage primaryStage) {"); 82 contents.add(" primaryStage.setTitle(\"Hello World!\");"); 83 contents.add(" Button btn = new Button();"); 84 contents.add(" btn.setText(\"Say 'Hello World'\");"); 85 contents.add(" btn.setOnAction(new EventHandler<ActionEvent>() {"); 86 contents.add(" @Override"); 87 contents.add(" public void handle(ActionEvent event) {"); 88 contents.add(" System.out.println(\"Hello World!\");"); 89 contents.add(" }"); 90 contents.add(" });"); 91 contents.add(" StackPane root = new StackPane();"); 92 contents.add(" root.getChildren().add(btn);"); 93 contents.add(" primaryStage.setScene(new Scene(root, 300, 250));"); 94 contents.add("// primaryStage.show(); no GUI for auto tests. "); 95 contents.add(" System.out.println(\"HelloWorld.primaryStage.show();\");"); 96 contents.add(" System.out.println(\"Parameters:\");" ); 97 contents.add(" for(String p : getParameters().getUnnamed())"); 98 contents.add(" System.out.println(\"parameter: \" + p );" ); 99 contents.add(" System.exit(0);"); 100 contents.add("}"); 101 contents.add("}"); 102 103 // Create and compile java source. 104 MainJavaFile = new File(mainClass + JAVA_FILE_EXT); 105 createFile(MainJavaFile, contents); 106 compile("-d", ".", mainClass + JAVA_FILE_EXT); 107 } catch (java.io.IOException ioe) { 108 ioe.printStackTrace(); 109 throw new RuntimeException("Failed creating HelloWorld."); 110 } 111 } 112 113 /* 114 * Create class that extends HelloWorld instead of Application 115 */ 116 static void createExtJavaFile(String mainmethod) { 117 try { 118 String mainClass = "ExtHello"; 119 List<String> contents = new ArrayList<>(); 120 contents.add("package helloworld;"); 121 contents.add("public class ExtHello extends HelloWorld {"); 122 contents.add(mainmethod); 123 contents.add("}"); 124 // Create and compile java source. 125 MainJavaFile = new File(mainClass + JAVA_FILE_EXT); 126 createFile(MainJavaFile, contents); 127 compile("-cp", ".", "-d", ".", mainClass + JAVA_FILE_EXT); 128 } catch (java.io.IOException ioe) { 129 ioe.printStackTrace(); 130 throw new RuntimeException("Failed creating ExtHello."); 131 } 132 } 133 134 /* 135 * Create non-JavaFX class for testing if jfxrt.jar is being loaded 136 * when it shouldn't be 137 */ 138 static void createNonFXJavaFile() { 139 try { 140 String mainClass = "HelloJava"; 141 List<String> contents = new ArrayList<>(); 142 contents.add("package helloworld;"); 143 contents.add("public class HelloJava {"); 144 contents.add(" public static void main(String[] args) {"); 145 contents.add(" for(String aa : args)"); 146 contents.add(" System.out.println(\"arg: \" + aa);" ); 147 contents.add(" }"); 148 contents.add("}"); 149 // Create and compile java source. 150 MainJavaFile = new File(mainClass + JAVA_FILE_EXT); 151 createFile(MainJavaFile, contents); 152 compile("-cp", ".", "-d", ".", mainClass + JAVA_FILE_EXT); 153 } catch (java.io.IOException ioe) { 154 ioe.printStackTrace(); 155 throw new RuntimeException("Failed creating HelloJava."); 156 } 157 } 158 159 // Create manifest for test fx application 160 static List<String> createManifestContents(String mainClassEntry, String fxMainEntry) { 161 List<String> mcontents = new ArrayList<>(); 162 mcontents.add("Manifest-Version: 1.0"); 163 mcontents.add("Created-By: FXLauncherTest"); 164 if (mainClassEntry != null) { 165 mcontents.add("Main-Class: " + mainClassEntry); 166 System.out.println("Main-Class: " + mainClassEntry); 167 } 168 if (fxMainEntry != null) { 169 mcontents.add("JavaFX-Application-Class: " + fxMainEntry); 170 System.out.println("JavaFX-Application-Class: " + fxMainEntry); 171 } 172 return mcontents; 173 } 174 175 // Method to marshal createJar to TestHelper.createJar() 176 static void createJar(File theJar, File manifestFile) { 177 createJar("cvmf", manifestFile.getName(), 178 theJar.getAbsolutePath(), "helloworld"); 179 } 180 181 static void saveFile(String tname, int testcount, File srcFile) { 182 File newFile = new File(tname + "-" + testcount + "-" + srcFile.getName()); 183 System.out.println("renaming " + srcFile.getName() + 184 " to " + newFile.getName()); 185 srcFile.renameTo(newFile); 186 } 187 188 static void cleanupFiles() throws IOException { 189 for(File f : ScratchDir.listFiles()) { 190 recursiveDelete(f); 191 } 192 } 193 194 static void checkStatus(TestResult tr, String testName, int testCount, 195 String mainclass) throws Exception { 196 if (tr.testStatus) { 197 System.out.println("PASS: " + testName + ":" + testCount + 198 " : test with " + mainclass); 199 cleanupFiles(); 200 } else { 201 saveFile(testName, testcount, FXtestJar); 202 System.out.println("FAIL: " + testName + ":" + testCount + 203 " : test with " + mainclass); 204 cleanupFiles(); 205 System.err.println(tr); 206 throw new Exception("Failed: " + testName + ":" + testCount); 207 } 208 } 209 210 /* 211 * Set Main-Class and iterate main_methods. 212 * Try launching with both -jar and -cp methods, with and without FX main 213 * class manifest entry. 214 * All cases should run. 215 * 216 * See sun.launcher.LauncherHelper$FXHelper for more details on how JavaFX 217 * applications are launched. 218 */ 219 @Test 220 static void testBasicFXApp() throws Exception { 221 testBasicFXApp(true, false); // -cp, no JAC 222 testBasicFXApp(false, true); // -jar, with JAC 223 testBasicFXApp(false, false); // -jar, no JAC 224 } 225 226 static void testBasicFXApp(boolean useCP, boolean setFXMainClass) throws Exception { 227 String testname = "testBasicFXApp"; 228 if (useCP) { 229 testname = testname.concat("_useCP"); 230 } 231 String fxMC = StdMainClass; 232 if (!setFXMainClass) { 233 testname = testname.concat("_noJAC"); 234 fxMC = null; 235 } 236 for (String mm : MAIN_METHODS) { 237 testcount++; 238 line(); 239 System.out.println("test# " + testcount + "- Main method: " + mm); 240 createJavaFile(mm); 241 createFile(ManifestFile, createManifestContents(StdMainClass, fxMC)); 242 createJar(FXtestJar, ManifestFile); 243 String sTestJar = FXtestJar.getAbsolutePath(); 244 final TestResult tr; 245 if (useCP) { 246 tr = doExec(javaCmd, "-cp", sTestJar, StdMainClass, APP_PARMS[0], APP_PARMS[1]); 247 } else { 248 tr = doExec(javaCmd, "-jar", sTestJar, APP_PARMS[0], APP_PARMS[1]); 249 } 250 tr.checkPositive(); 251 if (tr.testStatus && tr.contains("HelloWorld.primaryStage.show()")) { 252 for (String p : APP_PARMS) { 253 if (!tr.contains(p)) { 254 System.err.println("ERROR: Did not find " 255 + p + " in output!"); 256 } 257 } 258 } 259 checkStatus(tr, testname, testcount, StdMainClass); 260 } 261 } 262 263 /* 264 * Set Main-Class and iterate main methods. 265 * Main class extends another class that extends Application. 266 * Try launching with both -jar and -cp methods. 267 * All cases should run. 268 */ 269 @Test 270 static void testExtendFXApp() throws Exception { 271 testExtendFXApp(true, false); // -cp, no JAC 272 testExtendFXApp(false, true); // -jar, with JAC 273 testExtendFXApp(false, false); // -jar, no JAC 274 } 275 276 static void testExtendFXApp(boolean useCP, boolean setFXMainClass) throws Exception { 277 String testname = "testExtendFXApp"; 278 if (useCP) { 279 testname = testname.concat("_useCP"); 280 } 281 String fxMC = ExtMainClass; 282 if (!setFXMainClass) { 283 testname = testname.concat("_noJAC"); 284 fxMC = null; 285 } 286 for (String mm : MAIN_METHODS) { 287 testcount++; 288 line(); 289 System.out.println("test# " + testcount + "- Main method: " + mm); 290 createJavaFile(mm); 291 createExtJavaFile(mm); 292 createFile(ManifestFile, createManifestContents(ExtMainClass, fxMC)); 293 createJar(FXtestJar, ManifestFile); 294 String sTestJar = FXtestJar.getAbsolutePath(); 295 final TestResult tr; 296 if (useCP) { 297 tr = doExec(javaCmd, "-cp", sTestJar, ExtMainClass, APP_PARMS[0], APP_PARMS[1]); 298 } else { 299 tr = doExec(javaCmd, "-jar", sTestJar, APP_PARMS[0], APP_PARMS[1]); 300 } 301 tr.checkPositive(); 302 if (tr.testStatus && tr.contains("HelloWorld.primaryStage.show()")) { 303 for (String p : APP_PARMS) { 304 if (!tr.contains(p)) { 305 System.err.println("ERROR: Did not find " 306 + p + " in output!"); 307 } 308 } 309 } 310 checkStatus(tr, testname, testcount, ExtMainClass); 311 } 312 } 313 314 /* 315 * Ensure we can NOT launch a FX app jar with no Main-Class manifest entry 316 */ 317 @Test 318 static void testMissingMC() throws Exception { 319 String testname = "testMissingMC"; 320 testcount++; 321 line(); 322 System.out.println("test# " + testcount + ": abort on missing Main-Class"); 323 createJavaFile(" "); // no main() needed 324 createFile(ManifestFile, createManifestContents(null, StdMainClass)); // No MC, but supply JAC 325 createJar(FXtestJar, ManifestFile); 326 String sTestJar = FXtestJar.getAbsolutePath(); 327 TestResult tr = doExec(javaCmd, "-jar", sTestJar, APP_PARMS[0], APP_PARMS[1]); 328 tr.checkNegative(); // should abort if no Main-Class 329 if (tr.testStatus) { 330 if (!tr.contains("no main manifest attribute")) { 331 System.err.println("ERROR: launcher did not abort properly"); 332 } 333 } else { 334 System.err.println("ERROR: jar executed with no Main-Class!"); 335 } 336 checkStatus(tr, testname, testcount, StdMainClass); 337 } 338 339 /* 340 * test to ensure that we don't load any extraneous fx jars when 341 * launching a standard java application 342 * Test both -cp and -jar methods since they use different code paths. 343 * Neither case should cause jfxrt.jar to be loaded. 344 */ 345 @Test 346 static void testExtraneousJars() throws Exception { 347 testExtraneousJars(true); 348 testExtraneousJars(false); 349 } 350 351 static void testExtraneousJars(boolean useCP) throws Exception { 352 String testname = "testExtraneousJars"; 353 if (useCP) { 354 testname = testname.concat("_useCP"); 355 } 356 testcount++; 357 line(); 358 System.out.println("test# " + testcount 359 + ": test for erroneous jfxrt.jar loading"); 360 createNonFXJavaFile(); 361 createFile(ManifestFile, createManifestContents(NonFXMainClass, null)); 362 createJar(FXtestJar, ManifestFile); 363 String sTestJar = FXtestJar.getAbsolutePath(); 364 final TestResult tr; 365 366 if (useCP) { 367 tr = doExec(javaCmd, "-verbose:class", "-cp", sTestJar, NonFXMainClass, APP_PARMS[0], APP_PARMS[1]); 368 } else { 369 tr = doExec(javaCmd, "-verbose:class", "-jar", sTestJar, APP_PARMS[0], APP_PARMS[1]); 370 } 371 tr.checkPositive(); 372 if (tr.testStatus) { 373 if (!tr.notContains("jfxrt.jar")) { 374 System.out.println("testing for extraneous jfxrt jar"); 375 System.out.println(tr); 376 throw new Exception("jfxrt.jar is being loaded, it should not be!"); 377 } 378 if (!tr.notContains("sun.launcher.LauncherHelper$FXHelper")) { 379 System.out.println("testing for extraneous 'sun.launcher.LauncherHelper$FXHelper'"); 380 System.out.println(tr); 381 throw new Exception("FXHelper is being loaded, it should not be!"); 382 } 383 for (String p : APP_PARMS) { 384 if (!tr.contains(p)) { 385 System.err.println("ERROR: Did not find " 386 + p + " in output!"); 387 } 388 } 389 } 390 checkStatus(tr, testname, testcount, NonFXMainClass); 391 } 392 393 public static void main(String... args) throws Exception { 394 //check if fx is part of jdk 395 Class<?> fxClass = null; 396 try { 397 fxClass = Class.forName(FX_MARKER_CLASS); 398 } catch (ClassNotFoundException ex) { 399 // do nothing 400 } 401 if (fxClass != null) { 402 FXLauncherTest fxt = new FXLauncherTest(); 403 fxt.run(args); 404 if (testExitValue > 0) { 405 System.out.println("Total of " + testExitValue 406 + " failed. Test cases covered: " 407 + FXLauncherTest.testcount); 408 System.exit(1); 409 } else { 410 System.out.println("All tests pass. Test cases covered: " 411 + FXLauncherTest.testcount); 412 } 413 } else { 414 System.err.println("Warning: JavaFX components missing or not supported"); 415 System.err.println(" test passes vacuously."); 416 } 417 } 418 }