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 }