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 }