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