1 /*
   2  * Copyright (c) 2009, 2016, 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. Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  */
  24 
  25 package client.test.runner;
  26 
  27 import client.test.ExcludeRunModeMethod;
  28 import client.test.ExcludeRunModeType;
  29 import client.test.OnlyRunModeMethod;
  30 import client.test.OnlyRunModeType;
  31 import client.test.RunModes;
  32 import client.test.RunModes.RunModeException;
  33 import client.test.runner.CommonTestRunnerWorker.Command;
  34 import client.test.runner.CommonTestRunnerWorker.Command.CommandType;
  35 import client.test.runner.interview.LookAndFeelQuestion;
  36 import client.test.runner.interview.PipelineQuestion;
  37 import client.util.CtrUtils;
  38 import static client.util.CtrUtils.*;
  39 import client.util.CtrUtils.OutputReader;
  40 import client.util.JettyServer;
  41 import com.sun.interview.YesNoQuestion;
  42 import com.sun.javatest.Status;
  43 import com.sun.javatest.TestDescription;
  44 import com.sun.javatest.TestEnvironment;
  45 import com.sun.javatest.TestEnvironment.Fault;
  46 import java.io.*;
  47 import java.lang.reflect.Method;
  48 import java.net.ServerSocket;
  49 import java.net.Socket;
  50 import java.net.URL;
  51 import java.net.UnknownHostException;
  52 import java.util.concurrent.Semaphore;
  53 import java.util.concurrent.TimeUnit;
  54 import java.util.logging.Level;
  55 import java.util.logging.Logger;
  56 import org.junit.runner.RunWith;
  57 import test.javaclient.shared.CanvasRunner;
  58 import test.javaclient.shared.Utils;
  59 
  60 /**
  61  *
  62  * @author shura, mrkam, Sergey Grinev, Victor Shubov
  63  */
  64 public class TestScript extends htmltestrunner.TestScript {
  65 
  66     private static final int FORCED_TERMINATION_TIMEOUT = 5000;
  67     private static final boolean verbose = true; //TODO: use real logger
  68     private volatile Process process = null;
  69     private volatile ServerSocket cmdServer = null;
  70     private volatile Socket cmdSocket;
  71     private volatile ObjectOutputStream commandStream;
  72     private volatile TestRunner.Status status = null;
  73     private volatile TestEnvironment savedEnv;
  74     private Thread resultThread;
  75 
  76     @Override
  77     protected synchronized void interrupt(Status status) {
  78         System.out.println("interrupting with status " + status);
  79         try {
  80             if (process.isAlive()) {
  81                 process.destroyForcibly().waitFor(FORCED_TERMINATION_TIMEOUT, TimeUnit.MILLISECONDS);
  82             }
  83         } catch (InterruptedException ex) {
  84             System.err.println("Exception while interrupting test process: " + ex.getMessage());
  85         }
  86         if (resultThread.isAlive()) {
  87             resultThread.interrupt();
  88         }
  89         super.interrupt(status);
  90     }
  91 
  92     @Override
  93     protected synchronized Status getStatus() {
  94         return super.getStatus();
  95     }
  96 
  97     @Override
  98     protected void before(TestDescription description, String resultDir) throws Throwable {
  99         String testClassName = td.getParameter(RunUITestFinder.UNIT_TEST_CLASS_NAME);
 100         String testName = td.getParameter(RunUITestFinder.TEST_NAME);
 101         final String runMode = savedEnv.lookup(BasicFXInterview.RUN_MODE_PARAM)[0];
 102 
 103         System.out.println("\nTEST: " + testClassName + "/" + testName);
 104         System.out.println("Mode:" + runMode);
 105         System.out.println("Result Dir:" + resultDir);
 106         if (needToRun(testClassName, testName, runMode)) {
 107 
 108             try {
 109                 int masterPort = startServer();
 110 
 111                 if (runMode.equals(BasicFXInterview.RUN_MODE_DESKTOP)
 112                         || runMode.equals(BasicFXInterview.RUN_MODE_DESKTOP_SWING_INTEROPERABILITY)
 113                         || runMode.equals(BasicFXInterview.RUN_MODE_DESKTOP_SWT_INTEROPERABILITY)) {
 114                     runTd(description, resultDir, masterPort);
 115                 } else {
 116                     runPlugin(td, resultDir, BasicFXInterview.RUN_MODE_JNLP.equals(runMode));
 117                 }
 118             } catch (Throwable e) {
 119                 e.printStackTrace(System.err);
 120                 if (cmdServer != null) {
 121                     try {
 122                         cmdServer.close();
 123                     } catch (IOException ex) {
 124                         ex.printStackTrace(System.err);
 125                     }
 126                 }
 127                 if (cmdSocket != null) {
 128                     try {
 129                         cmdSocket.close();
 130                     } catch (IOException ex) {
 131                         ex.printStackTrace(System.err);
 132                     }
 133                 }
 134                 if (commandStream != null) {
 135                     try {
 136                         commandStream.close();
 137                     } catch (IOException ex) {
 138                         ex.printStackTrace(System.err);
 139                     }
 140                 }
 141                 if (process != null) {
 142                     process.destroy();
 143                 }
 144                 commandStream.close();
 145                 interrupt(Status.error(e.toString()));
 146             }
 147 
 148             resultThread = new Thread(new Runnable() {
 149                 @Override
 150                 public void run() {
 151                     try {
 152                         System.err.println("Waiting for exit");
 153                         ObjectInputStream ois = new ObjectInputStream(cmdSocket.getInputStream());
 154                         status = (TestRunner.Status) ois.readObject();
 155 
 156                         Status jtStatus;
 157                         System.err.println("Process returned status " + status);
 158                         if (status == null) {
 159                             jtStatus = Status.error("Unexpected status: " + status);
 160                         } else if (status.isPassed()) {
 161                             jtStatus = Status.passed(status.getText());
 162                         } else if (status.isFailed()) {
 163                             jtStatus = Status.failed(status.getText());
 164                         } else {
 165                             jtStatus = Status.error(status.getText());
 166                         }
 167                         interrupt(jtStatus);
 168 
 169                     } catch (Throwable ex) {
 170                         ex.printStackTrace(System.err);
 171                         interrupt(Status.error(CtrUtils.stackTraceToString(ex)));
 172                     } finally {
 173 
 174                         //finalize
 175                         try {
 176                             commandStream.close();
 177                             cmdSocket.close();
 178                             cmdServer.close();
 179                         } catch (IOException ex) {
 180                             ex.printStackTrace();
 181                         }
 182 
 183                         if (runMode.equals(BasicFXInterview.RUN_MODE_PLUGIN)) {
 184                             System.err.println("closing browser");
 185                             if (process != null) {
 186                                 process.destroy();
 187                             }
 188                         }
 189                         try {
 190                             if (process != null) {
 191                                 process.waitFor();
 192                             }
 193                         } catch (InterruptedException ex) {
 194                             ex.printStackTrace(System.err);
 195                         }
 196 //                    if (jemmyProcess != null) {
 197 //                        System.out.println("killed jemmy process");
 198 //                        try {
 199 //                            jemmyProcess.getInputStream().close();
 200 //                        } catch (IOException ex) {
 201 //                            ex.printStackTrace();
 202 //                        }
 203 //
 204 //                        jemmyProcess.destroy();
 205 //                    }
 206                         System.out.println("DONE");
 207                     }
 208                 }
 209             }, "I'm waiting for test's result");
 210             resultThread.start();
 211         }
 212     }
 213 
 214     @Override
 215     protected void showTestDialog(TestDescription td, TestEnvironment env) {
 216         String nodescription = td.getParameter(RunUITestFinder.NO_DESCRIPTION);
 217         boolean dryRun = YesNoQuestion.YES.equals(lookup(BasicFXInterview.DRY_RUN_TAG, YesNoQuestion.NO));
 218         if (!Boolean.parseBoolean(nodescription)) {
 219             if (dryRun) {
 220                 System.err.println("DRY RUN MODE");
 221                 int delay = Integer.parseInt(lookup(BasicFXInterview.DRY_RUN_DURATION_TAG, BasicFXInterview.DRY_RUN_DURATION_TAG.toString()));
 222                 try {
 223                     Thread.sleep(delay * 1000);
 224                 } catch (InterruptedException ex) {
 225                 }
 226                 setStatus(Status.error("Dry Run"));
 227             } else {
 228                 super.showTestDialog(td, env);
 229             }
 230         }
 231     }
 232 
 233     /**
 234      * Never put any cleanup here as this method would be called only in case of
 235      * success
 236      * @throws java.lang.Throwable
 237      */
 238     @Override
 239     protected void after(TestDescription description, String resultDir) throws Throwable {
 240     }
 241 
 242     @Override
 243     public Status run(String[] args, TestDescription td, TestEnvironment env) {
 244         savedEnv = env;
 245         try {
 246             String testClassName = td.getParameter(RunUITestFinder.UNIT_TEST_CLASS_NAME);
 247             String testName = td.getParameter(RunUITestFinder.TEST_NAME);
 248             final String runMode = savedEnv.lookup(BasicFXInterview.RUN_MODE_PARAM)[0];
 249             if (!needToRun(testClassName, testName, runMode)) {
 250                 return Status.passed("Not run in " + runMode + " mode");
 251             }
 252         } catch (RunModes.RunModeException ex) {
 253             return Status.error(ex.toString());
 254         } catch (NoSuchMethodException ex) {
 255             return Status.error(ex.toString());
 256         } catch (ClassNotFoundException ex) {
 257             return Status.error(ex.toString());
 258         } catch (Fault ex) {
 259             return Status.error(ex.toString());
 260         }
 261         super.run(args, td, env);
 262         String hasCheckUI = td.getParameter(RunUITestFinder.HAS_CHECK_UI);
 263         try {
 264             if (Boolean.parseBoolean(hasCheckUI)) {
 265                 sendCommand(CommandType.CHECK_UI_AND_EXIT);
 266             } else {
 267                 try {
 268                     sendCommand(CommandType.EXIT);
 269                 } catch (IOException ex) {
 270                     //this is valid for UI to exit before we asked for
 271                 }
 272             }
 273             resultThread.join(600000);
 274             if (status == null) {
 275                 sendCommand(CommandType.ABORT);
 276                 setStatus(Status.error("process didn't return valid status"));
 277             }
 278         } catch (Throwable ex) {
 279             return Status.error(ex.toString());
 280         }
 281 
 282         System.out.println("We are done with " + td.getName() + ". Status: " + getStatus());
 283         return getStatus();
 284     }
 285 
 286     private String getJvmArgPrismOrder() {
 287         final String pipelineOptions = lookup(PipelineQuestion.PIPELINE_PARAM_NAME, "");
 288         String jvmArgPrismOrder = "";
 289         if (!pipelineOptions.trim().equals("")) {
 290             jvmArgPrismOrder = "-Dprism.order=" + pipelineOptions;
 291         }
 292         return jvmArgPrismOrder;
 293     }
 294 
 295     /**
 296      *
 297      * @param td
 298      * @param resultDir
 299      * @return
 300      * @throws IOException
 301      * @throws Fault
 302      */
 303     protected String[] tdCmdArgs(TestDescription td, String resultDir, int port) throws IOException, Fault {
 304         String testClassName = td.getParameter(RunUITestFinder.UNIT_TEST_CLASS_NAME);
 305 
 306         boolean isJunit = Boolean.parseBoolean(td.getParameter(RunUITestFinder.TYPE_JUNIT));
 307 
 308         //String fxSdkHome = td.getParameter(BasicFXInterview.FX_SDK_HOME_PARAM_NAME);
 309         String fxSdkHome = savedEnv.lookup(BasicFXInterview.FX_SDK_HOME_PARAM_NAME)[0];
 310 
 311         String[] pathArr = savedEnv.lookup(BasicFXInterview.JAVA_PARAM_NAME);
 312         StringBuilder pathStr = new StringBuilder();
 313         for (String p : pathArr) {
 314             if (pathStr.length() > 0) {
 315                 pathStr.append(" ");
 316             }
 317             pathStr.append(p);
 318         }
 319         String javaExec = pathStr.toString();
 320 
 321 
 322         String proxy = lookup(BasicFXInterview.PROXY_PARAM_NAME, "");
 323         String jvmProxyHost = "";
 324         String jvmProxyPort = "";
 325         if (proxy != null && proxy.trim().length() > 0) {
 326             URL proxyUrl = getProxyUrl(proxy);
 327             if (proxyUrl != null) {
 328                 jvmProxyHost = "-DproxyHost=" + proxyUrl.getHost();
 329                 jvmProxyPort = "-DproxyPort=" + proxyUrl.getPort();
 330             }
 331         }
 332 
 333         String additionalOptions = getAdditionalOptions();
 334 
 335         String[] jvmVmOptions = savedEnv.lookup(BasicFXInterview.VM_OPTIONS_PARAM_NAME);
 336 
 337         String jvmArgPrismOrder = getJvmArgPrismOrder();
 338 
 339         //TODO (SG): is this still required?
 340         String jvmArgLibraryPath = "";
 341         if (fxSdkHome != null) {
 342             jvmArgLibraryPath = "-Djava.library.path=" + fxSdkHome + File.separator + "rt";
 343         }
 344 
 345 //        String externaloutput = lookup(BasicFXInterview.EXTERNALOUTPUT, "");
 346 //        if (externaloutput != null) {
 347 //            jvmArgImageUtils = "-Dimageutils.outputpath=" + externaloutput + File.separator;
 348 //        }
 349         String jvmArgImageUtils = "-Dimageutils.outputpath=" + resultDir + File.separator;
 350 
 351         String jvmArgNoDesc = "";
 352         if (Boolean.parseBoolean(td.getParameter(RunUITestFinder.NO_DESCRIPTION))) {
 353             jvmArgNoDesc = "-Djavatest.mode.nodesc=true";
 354         }
 355 
 356         String runMode = lookup(BasicFXInterview.RUN_MODE_PARAM, BasicFXInterview.RUN_MODE_DESKTOP);
 357 
 358         String jvmInterop = "";
 359         if (runMode.equals(BasicFXInterview.RUN_MODE_DESKTOP_SWING_INTEROPERABILITY)) {
 360             jvmInterop = "-Djavafx.swinginteroperability=true";
 361         } else if (runMode.equals(BasicFXInterview.RUN_MODE_DESKTOP_SWT_INTEROPERABILITY)) {
 362             jvmInterop = "-Djavafx.swtinteroperability=true";
 363         }
 364 
 365         String lookAndFeelOptions = "";
 366         String lfOptions = lookup(LookAndFeelQuestion.LOOKANDFEEL_PARAM_NAME, "");
 367         if (!lfOptions.trim().equals("")) {
 368             lookAndFeelOptions = "-Djavafx.userAgentStylesheetUrl=" + lfOptions;
 369         }
 370 
 371         //Needed to get images via proxy in Evergreen via VPN see RT-21325 || RT-19661
 372         final boolean isFXCompatibility = System.getProperty("java.class.path").contains("JavaFXCompatibility");
 373         String ipV4 = isFXCompatibility ? "-Djava.net.preferIPv4Stack=true" : "";
 374 
 375         String swtTestOpt = "";
 376         try {
 377             if (System.getProperty("os.name").toLowerCase().indexOf("mac") >= 0) {
 378                 RunWith wunWithAnnotation = Class.forName(testClassName).getAnnotation(RunWith.class);
 379                 if ((wunWithAnnotation != null) && (wunWithAnnotation.value().equals(CanvasRunner.class))) {
 380                     swtTestOpt = "-XstartOnFirstThread";
 381                     System.out.println("Use -XstartOnFirstThread option, as we on MacOS, and SWT test is run.");
 382                 }
 383             }
 384         } catch (ClassNotFoundException ex) {
 385             System.err.println("Error : " + ex);
 386         }
 387 
 388         String jvmArgClientTestRoot = "-DtestRoot=" + RunUITestFinder.testRoot;
 389 
 390         String[] command = new String[]{};
 391         command = addToArray(command, javaExec.trim());
 392         command = addToArray(command, jvmVmOptions);
 393         command = addToArray(command, lookAndFeelOptions);
 394         command = addToArray(command, ipV4);
 395         command = addToArray(command, jvmArgPrismOrder, jvmArgLibraryPath, jvmArgImageUtils);
 396         command = addToArray(command, additionalOptions);
 397         command = addToArray(command, jvmArgNoDesc, jvmProxyHost, jvmProxyPort, jvmInterop, swtTestOpt);
 398         command = addToArray(command, jvmArgClientTestRoot);
 399         command = addToArray(command, "-DmasterPort=" + port);
 400         command = addToArray(command, "-classpath", System.getProperty("java.class.path"));
 401 //        command = addToArray(command, "-Xdebug", "-Xnoagent", "-Djava.compiler=NONE", "-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5858");
 402         command = addToArray(command, isJunit ? JUnit2TestRunner.class.getName() : TestRunner.class.getName(), testClassName);
 403         return command;
 404     }
 405 
 406     /**
 407      *
 408      * @param td
 409      * @param resultDir
 410      * @return
 411      */
 412     protected String[] pluginCmdArgs(TestDescription td, String resultDir) {
 413         return null;
 414     }
 415 
 416     /**
 417      *
 418      * @param command
 419      * @throws IOException
 420      */
 421     protected void doRunTd(String[] command) throws IOException {
 422         process = Runtime.getRuntime().exec(deleteEmptyElements(command));
 423     }
 424 
 425     private int startServer() throws IOException {
 426         System.out.println("Starting server." );
 427         int iRetryCount = 3;
 428         boolean bindDone = false;
 429         while ( (iRetryCount > 0) && (false==bindDone) ) {
 430             iRetryCount = iRetryCount - 1;
 431 
 432             try {
 433                 cmdServer = new ServerSocket(0);
 434                 System.out.println("Started server at port " + cmdServer.getLocalPort());
 435                 bindDone = true;
 436             } catch (java.net.BindException be) {
 437                 bindDone = false;
 438                 System.out.println("  === bind exception ===");
 439                 Socket socket = new Socket("127.0.0.1", cmdServer.getLocalPort());
 440                 commandStream = new ObjectOutputStream(socket.getOutputStream());
 441                 sendCommand(CommandType.ABORT);
 442                 try {Thread.sleep(100);} catch(Exception e){}
 443                 }
 444 
 445         }// retry loop end
 446         cmdServer.setSoTimeout(60000); // we need to be generous for plugin mode, it gets to "download" runnable
 447         return cmdServer.getLocalPort();
 448     }
 449 
 450     private void waitForConnection() throws IOException {
 451         System.out.println("Waiting for connection...");
 452         cmdSocket = cmdServer.accept();
 453         commandStream = new ObjectOutputStream(cmdSocket.getOutputStream());
 454         System.out.println("Connected");
 455     }
 456 
 457     private void sendCommand(CommandType command) throws UnknownHostException, IOException {
 458         sendCommand(command, "-");
 459     }
 460 
 461     private synchronized void sendCommand(CommandType command, String param) throws IOException {
 462         Command trc = new Command(command, param);
 463         if (verbose) {
 464             System.out.println("command " + trc);
 465         }
 466         commandStream.writeObject(trc);
 467     }
 468 
 469     /**
 470      *
 471      * @param id
 472      * @param def
 473      * @return
 474      */
 475     protected String lookup(String id, String def) {
 476         String[] result = null;
 477         try {
 478             result = savedEnv.lookup(id);
 479         } catch (Fault ex) {
 480             ex.printStackTrace(System.err);
 481         }
 482         return result == null || result.length == 0 ? def : result[0];
 483     }
 484 
 485     /**
 486      *
 487      * @param id
 488      * @return
 489      */
 490     protected String[] lookup(String id) {
 491         String[] result = null;
 492         try {
 493             result = savedEnv.lookup(id);
 494         } catch (Fault ex) {
 495             ex.printStackTrace(System.err);
 496         }
 497         return result;
 498     }
 499 
 500     private void runTd(TestDescription td, String resultDir, int port) throws IOException, InterruptedException, Fault {
 501         String[] command = tdCmdArgs(td, resultDir, port);
 502         dumpProcessExecution(resultDir, command, null);
 503         doRunTd(command);
 504         System.out.println("Logs: " + resultDir + File.separator);
 505 
 506         if (process != null) {
 507             //TODO: who close that streams?
 508             new Thread(new OutputReader(new FileOutputStream(resultDir + File.separator + "process.out"), process.getInputStream())).start();
 509             new Thread(new OutputReader(new FileOutputStream(resultDir + File.separator + "process.err"), process.getErrorStream())).start();
 510         } else {
 511             System.out.println("ERROR: Failed to create process"); // TODO: fail test?
 512         }
 513         waitForConnection();
 514         String testName = td.getParameter(RunUITestFinder.TEST_NAME);
 515         String testClassName = td.getParameter(RunUITestFinder.UNIT_TEST_CLASS_NAME);
 516         sendCommand(CommandType.SET_TEST_CLASS, testClassName);
 517         if (testName != null) {
 518             sendCommand(CommandType.SET_TEST_NAME, testName);
 519         }
 520         sendCommand(CommandType.RUN_TEST);
 521     }
 522 
 523     private static void dumpProcessExecution(String resultDir, String[] cmd, String workdir) throws IOException {
 524         final PrintWriter writer = new PrintWriter(
 525                 new FileWriter(resultDir + File.separator + "process_execution.log"));
 526         writer.println("starting process...");
 527         writer.println("Command: ");
 528         for (String s : cmd) {
 529             writer.print(s);
 530             writer.print("\n");
 531         }
 532         writer.println("");
 533         if (workdir != null) {
 534             writer.println("Workdir: " + workdir);
 535         }
 536         writer.flush();
 537     }
 538     private static final String pluginPath = "./dist-plugin/";
 539     private static final String pluginFile = "JavaClientPluginTest";
 540 
 541     private void runPlugin(TestDescription td, String resultDir, boolean isJnlp)
 542             throws IOException, Fault {
 543         String testClassName = td.getParameter(RunUITestFinder.UNIT_TEST_CLASS_NAME);
 544         String testName = td.getParameter(RunUITestFinder.TEST_NAME);
 545 
 546         //TODO: add non-junit tests support
 547         boolean isJunit = Boolean.parseBoolean(td.getParameter(RunUITestFinder.TYPE_JUNIT));
 548         if (!isJunit) {
 549             throw new UnsupportedOperationException("runui is not supported yet");
 550         }
 551         String[] command;
 552         String param;
 553         File workdir = new File(System.getProperty("user.dir"));
 554         final int port = 8485;
 555         if (isJnlp) {
 556             String[] pathArr = savedEnv.lookup(BasicFXInterview.JAVAWS_PARAM_NAME);
 557             StringBuilder pathStr = new StringBuilder();
 558             for (String p : pathArr) {
 559                 if (pathStr.length() > 0) {
 560                     pathStr.append(" ");
 561                 }
 562                 pathStr.append(p);
 563             }
 564             File path = new File(pathStr.toString());
 565             param = new File(pluginPath + pluginFile + ".jnlp").getAbsolutePath();
 566             if (!path.exists()) {
 567                 throw new IllegalArgumentException("javaws path is invalid: " + pathStr);
 568             }
 569             workdir = path.getParentFile();
 570 
 571             String jvmArgPrismOrder = getJvmArgPrismOrder();
 572             if (jvmArgPrismOrder.length() > 0) {
 573                 command = new String[]{pathStr.toString(), "-J" + jvmArgPrismOrder};  // "-Dprism.order=sw"
 574             } else {
 575                 command = new String[]{pathStr.toString()};
 576             }
 577 
 578         } else { // plugin
 579             command = addBrowserTricks(savedEnv.lookup(BasicFXInterview.BROWSER_PARAM_NAME));
 580             JettyServer.getInstance(port).setBaseDir(new File(pluginPath).getAbsolutePath());
 581             param = "http://localhost:" + port + "/" + pluginFile + ".html";
 582         }
 583         if (verbose) {
 584             for (String string : command) {
 585                 System.err.println("cmd: " + string);
 586             }
 587         }
 588         String[] merge = addToArray(command, param);
 589         dumpProcessExecution(resultDir, merge, workdir.getAbsolutePath());
 590         if (Utils.isMacOS()) {
 591             ensureJemmyServerRun(workdir.getAbsolutePath());
 592         }
 593         process = new ProcessBuilder(merge).directory(workdir).start();
 594         if (process == null) {
 595             throw new RuntimeException("Failed to create process for test ");
 596         }
 597         waitForConnection();
 598         sendCommand(CommandType.SET_OUT, resultDir + File.separator + "process.out");
 599         sendCommand(CommandType.SET_ERR, resultDir + File.separator + "process.err");
 600         sendCommand(CommandType.SET_TEST_CLASS, testClassName);
 601         sendCommand(CommandType.SET_BASEDIR, System.getProperty("user.dir"));
 602         sendCommand(CommandType.SET_ABSOLUTE_DIR, resultDir + File.separator);
 603         if (testName != null) {
 604             sendCommand(CommandType.SET_TEST_NAME, testName);
 605         }
 606         sendCommand(CommandType.RUN_TEST);
 607     }
 608 
 609     /**
 610      *
 611      * @param arr
 612      * @param st
 613      * @return
 614      */
 615     public static String[] addToArray(String[] arr, String... st) {
 616         String[] newArr = new String[arr.length + st.length];
 617         System.arraycopy(arr, 0, newArr, 0, arr.length);
 618         System.arraycopy(st, 0, newArr, arr.length, st.length);
 619         return newArr;
 620     }
 621 
 622     private static enum BROWSER {
 623 
 624         FIREFOX("firefox"), CHROME("chrome"), IE("iexplore"), SAFARI("safari"), UNKNOWN("\n");
 625         private final String key;
 626 
 627         private BROWSER(String key) {
 628             this.key = key;
 629         }
 630 
 631         public static BROWSER detect(String[] command) {
 632             for (int i = command.length - 1; i >= 0; i--) {
 633                 for (BROWSER browser : values()) {
 634                     if (command[i] != null && command[i].toLowerCase().contains(browser.key)) {
 635                         System.out.println("Detected Browser: " + browser.name());
 636                         return browser;
 637                     }
 638                 }
 639             }
 640             return UNKNOWN;
 641         }
 642     };
 643 
 644     private static String[] addBrowserTricks(String[] command) {
 645         switch (BROWSER.detect(command)) {
 646             case CHROME:
 647                 return addToArray(command, "--kiosk", "--incognito", "--disable-hang-monitor", "--always-authorize-plugins", "--allow-outdated-plugins");
 648             case FIREFOX:
 649                 return addToArray(command, "-new-window");
 650             case IE:
 651                 return addToArray(command, "-k");
 652             case SAFARI:
 653                 return new String[]{"/usr/bin/open", "-a", "Safari"};
 654         }
 655         return command;
 656     }
 657 
 658     private boolean needToRun(String testClassName, String testName, String runMode) throws NoSuchMethodException, ClassNotFoundException, RunModeException {
 659         Class testClass = Class.forName(testClassName);
 660         RunModes mode = RunModes.parseString(runMode);
 661         if (mode == null) {
 662             throw new RunModes.RunModeException(runMode);
 663         }
 664         ExcludeRunModeType ermt = (ExcludeRunModeType) testClass.getAnnotation(ExcludeRunModeType.class);
 665         if (ermt != null) {
 666             if (ermt.value() == mode) {
 667                 return false;
 668             }
 669         }
 670         OnlyRunModeType ormt = (OnlyRunModeType) testClass.getAnnotation(OnlyRunModeType.class);
 671         if (ormt != null) {
 672             if (ormt.value() != mode) {
 673                 return false;
 674             }
 675         }
 676         if (testName == null) {
 677             // fox JavaFXCompatibility suite weird run mode
 678             return true;
 679         }
 680         //not testClass.getMethod(testName) because we can't get static method.
 681         Method[] methods = testClass.getMethods();
 682         for (Method testMethod : methods) {
 683             if (testName.equals(testMethod.getName())) {
 684                 ExcludeRunModeMethod ermm = (ExcludeRunModeMethod) testMethod.getAnnotation(ExcludeRunModeMethod.class);
 685                 if (ermm != null) {
 686                     if (ermm.value() == mode) {
 687                         return false;
 688                     }
 689                 }
 690                 OnlyRunModeMethod ormm = (OnlyRunModeMethod) testMethod.getAnnotation(OnlyRunModeMethod.class);
 691                 if (ormm != null) {
 692                     if (ormm.value() != mode) {
 693                         return false;
 694                     }
 695                 }
 696             }
 697         }
 698         return true;
 699     }
 700 
 701     /**
 702      *
 703      * @return
 704      */
 705     public Thread getProcessKiller() {
 706         return new ProcessKiller(5000L, process, cmdServer, cmdSocket);
 707     }
 708 
 709     private class ProcessKiller extends Thread {
 710 
 711         long sleepTime;
 712         Process targetProc;
 713         ServerSocket targetSerSocket;
 714         Socket targetSocket;
 715 
 716         public ProcessKiller(long time, Process process, ServerSocket targetSerSock, Socket targetSock) {
 717             this.sleepTime = time;
 718             this.targetProc = process;
 719             this.targetSerSocket = targetSerSock;
 720             this.targetSocket = targetSock;
 721         }
 722 
 723         @Override
 724         public void run() {
 725             try {
 726                 sleep(sleepTime);
 727             } catch (InterruptedException ex) {
 728                 Logger.getLogger(TestScript.class.getName()).log(Level.SEVERE, null, ex);
 729             }
 730             if (targetProc.isAlive()) {
 731                 System.out.println("Process has been interrupted forcibly!");
 732                 targetProc.destroyForcibly();
 733                 if (targetSerSocket != null) {
 734                     if (!targetSerSocket.isClosed()) {
 735                         try {
 736                             targetSerSocket.close();
 737                         } catch (IOException ex) {
 738                             System.out.println("CMDSERVER: exception while cloing");
 739                             ex.printStackTrace();
 740                         }
 741                     }
 742                 } else {
 743                     System.out.println("CMDSERVER: NULL");
 744                 }
 745                 if (targetSocket != null) {
 746                     if (!targetSocket.isClosed()) {
 747                         try {
 748                             targetSocket.close();
 749                         } catch (IOException ex) {
 750                             System.out.println("CMDSOCKET: exception while cloing");
 751                             ex.printStackTrace();
 752                         }
 753                     }
 754                 } else {
 755                     System.out.println("CMDSOCKET: NULL");
 756                 }
 757             }
 758         }
 759     }
 760 
 761     /**
 762      *
 763      * @return
 764      */
 765     protected TestEnvironment getSavedEnv() {
 766         return savedEnv;
 767     }
 768 
 769     /**
 770      *
 771      * @return
 772      */
 773     protected String getAdditionalOptions(){
 774         return "";
 775     }
 776 
 777 }