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