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