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 java.io.File;
  28 import java.io.IOException;
  29 import java.io.ObjectInputStream;
  30 import java.io.ObjectOutputStream;
  31 import java.io.PrintStream;
  32 import java.io.Serializable;
  33 import java.net.Socket;
  34 import java.util.concurrent.Semaphore;
  35 import org.junit.runner.JUnitCore;
  36 import org.junit.runner.Request;
  37 import org.junit.runner.Result;
  38 import org.junit.runner.notification.Failure;
  39 import org.junit.runner.notification.RunListener;
  40 import test.javaclient.shared.screenshots.GoldenImageManager;
  41 
  42 /**
  43  *
  44  * @author shura, mrkam, Sergey Grinev, Victor Shubov
  45  */
  46 public class CommonTestRunnerWorker {
  47 
  48     static final int DEFAULT_PORT = 56179;
  49 
  50     private final JUnitCore core = new JUnitCore();
  51     private Abstract2TestRunner testRunner;
  52     private final Semaphore uilock = new Semaphore(0);
  53     private Socket socket;
  54     private String testClassName;
  55     private String testName;
  56     private long testStart;
  57     private String host = "127.0.0.1";
  58     private boolean debugMode = false;
  59     private volatile boolean isExiting = false;
  60 
  61     private CommonTestRunnerWorker() {
  62     }
  63 
  64     /**
  65      *
  66      * @param _testRunner
  67      */
  68     public CommonTestRunnerWorker(Abstract2TestRunner _testRunner) {
  69         testRunner = _testRunner;
  70     }
  71 
  72     private final Thread exitThread = new Thread(new Runnable() {
  73         @Override
  74         public void run() {
  75             waitUI();
  76             exit(testRunner.getCurrentStatus());
  77         }
  78     }, "exit waiter");
  79 
  80     static int getTestPort() {
  81         return Integer.valueOf(System.getProperty("masterPort", String.valueOf(DEFAULT_PORT)));
  82     }
  83     /**
  84      *
  85      * @throws Throwable
  86      */
  87     public void runUI() throws Throwable {
  88         System.out.println("CommonTestRunnerWorker.runUI(): starting test...");
  89         core.addListener(new RunListener() {
  90 
  91             @Override
  92             public void testAssumptionFailure(Failure failure) {
  93                 System.out.println("junit failure: " + failure.getMessage());
  94                 failure.getException().printStackTrace();
  95                 exit(Abstract2TestRunner.Status.FAIL);
  96             }
  97 
  98             @Override
  99             public void testFailure(Failure failure) throws Exception {
 100                 System.out.println("junit failure: " + failure.getMessage());
 101                 failure.getException().printStackTrace();
 102                 exit(Abstract2TestRunner.Status.FAIL);
 103             }
 104 
 105         });
 106         Result r = core.run(Request.method(Class.forName(testClassName), testName));
 107         System.out.println("got result = " + r.wasSuccessful());
 108         exit(r.wasSuccessful() ? Abstract2TestRunner.Status.PASS : Abstract2TestRunner.Status.FAIL);
 109     }
 110 
 111     private void waitUI() {
 112         if (!uilock.tryAcquire()) {
 113             report("Waiting for UI to start or nodesc/auto test to finish");
 114             try {
 115                 uilock.acquire();
 116             } catch (InterruptedException ignored) {
 117                 ignored.printStackTrace();
 118             }
 119         }
 120         report("UI started successfully");
 121     }
 122 
 123     /**
 124      *
 125      * @param text
 126      */
 127     protected void report(String text) {
 128         long time = System.nanoTime() - testStart;
 129         System.out.printf("%dms: %s\n", time / 1000000, text);
 130     }
 131 
 132     private void handleEx(Throwable ex) {
 133         ex.printStackTrace();
 134         exit(Abstract2TestRunner.Status.ERROR);
 135     }
 136 
 137     /**
 138      *
 139      * @param args
 140      */
 141     protected final void setArgs(final String... args) {
 142         if (args != null && args.length > 0) {
 143             if (args.length == 1) {
 144                 testClassName = args[0];
 145             } else if (args.length >= 3 && args[0].equals("--debug")) {
 146                 System.err.println("debug mode");
 147                 debugMode = true;
 148                 testClassName = args[1];
 149                 testName = args[2];
 150             } else {
 151                 System.err.println("remote mode");
 152                 testClassName = args[0];
 153                 host = args[1];
 154             }
 155         }
 156         if (null == testClassName) {
 157             System.err.println("null == testClassName, argslen=" + args.length);
 158         }
 159     }
 160     
 161     /**
 162      *
 163      * @return
 164      */
 165     public String getTestClassName() {
 166         return testClassName;
 167     }
 168 
 169     /**
 170      *
 171      * @return
 172      */
 173     public String getTestName() {
 174         return testName;
 175     }
 176 
 177     /**
 178      *
 179      */
 180     protected final void prepareAndRunTest() {
 181 
 182         if (debugMode) {
 183             runTest();
 184             System.err.println("Test done.");
 185             return;
 186         }
 187 
 188         testStart = System.nanoTime();
 189         try {
 190             int port = getTestPort();
 191             report("Looking for server at host " + host + ", port " + port + "...");
 192             socket = new Socket(host, port);
 193             ObjectInputStream readerStream = new ObjectInputStream(socket.getInputStream());
 194             report("Connected");
 195 
 196             loop:
 197             do {
 198                 Command trc = (Command) readerStream.readObject();
 199                 report("Got command: " + trc.type + ":" + trc.param);
 200                 switch (trc.type) {
 201                     case SET_TEST_CLASS:
 202                         testClassName = trc.param;
 203                         break;
 204                     case SET_BASEDIR:
 205                         //Looks like method is required for some remote invocation and could be
 206                         //used later if it will be decided to run jnlp-mode again. 
 207                         //So commenting instead of removing.      
 208                         //GoldenImageManager.setBaseDir(trc.param);
 209                         break;
 210                     case SET_ABSOLUTE_DIR:
 211                         //Looks like method is required for some remote invocation and could be
 212                         //used later if it will be decided to run jnlp-mode again. 
 213                         //So commenting instead of removing.                              
 214                         //GoldenImageManager.setAbsoluteDir(trc.param);
 215                         break;
 216                     case SET_TEST_NAME:
 217                         testName = trc.param;
 218                         break;
 219                     case SET_ERR:
 220                         System.setErr(new PrintStream(new File(trc.param)));
 221                         break;
 222                     case SET_OUT:
 223                         System.setOut(new PrintStream(new File(trc.param)));
 224                         break;
 225                     case RUN_TEST:
 226                         if (testClassName == null) {
 227                         handleEx(new IllegalStateException("Running test without classname!"));
 228                         break loop;
 229                     } else {
 230                         runTest();
 231                     }
 232                         break;
 233                     case CHECK_UI_AND_EXIT:
 234                         try {
 235                         waitUI();
 236                         testRunner.checkUI();
 237                     } catch (Throwable ex) {
 238                         handleEx(ex);
 239                     }
 240                         exit(testRunner.getCurrentStatus());
 241                         break;
 242                     case EXIT:
 243                         // we want be able to wait for abort here
 244                         if (!exitThread.isAlive()) {
 245                         exitThread.start();
 246                     }
 247                         break;
 248                     case ABORT:
 249                         exit(Abstract2TestRunner.Status.ABORTED);
 250                         break;
 251                     default:
 252                         exit(Abstract2TestRunner.Status.UKNOWN_COMMAND);
 253                         break;
 254                 }
 255 
 256             } while (true);
 257             readerStream.close();
 258         } catch (Throwable ex) {
 259             if (!isExiting) {
 260                 handleEx(ex);
 261             }
 262         }
 263     }
 264 
 265     private void runTest() {
 266         testStart = System.nanoTime();
 267         new Thread(new Runnable() {
 268             @Override
 269             public void run() {
 270                 report("CommonTestRunnerWorker.runTest(): starting UI ...");
 271                 try {
 272                     testRunner.runUI();
 273                 } catch (Throwable ex) {
 274                     handleEx(ex);
 275                 } finally {
 276                     report("UI started");
 277                     uilock.release(1000);
 278                 }
 279             }
 280         }).start();
 281     }
 282 
 283     /**
 284      *
 285      * @param exitStatus
 286      */
 287     protected synchronized void exit(Abstract2TestRunner.Status exitStatus) {
 288         Abstract2TestRunner.Status exitValue = exitStatus;
 289         System.err.println("called exit " + exitValue);
 290 
 291         try {
 292             System.err.println("call stopui");
 293             testRunner.stopUI();
 294         } catch (Throwable ex) {
 295             ex.printStackTrace();
 296             exitValue = Abstract2TestRunner.Status.ERROR;
 297         }
 298 
 299         report("Exiting with status: " + exitValue);
 300         isExiting = true;
 301         if (socket != null && socket.isConnected()) {
 302             try {
 303                 new ObjectOutputStream(socket.getOutputStream()).writeObject(exitValue);
 304             } catch (IOException ex) {
 305                 ex.printStackTrace();
 306             }
 307         }
 308         System.exit(exitValue.getN());
 309     }
 310 
 311     static class Command implements Serializable {
 312     static enum CommandType {
 313         CHECK_UI_AND_EXIT,
 314         ABORT,
 315         STOP_UI,
 316         EXIT,
 317         SET_TEST_CLASS,
 318         SET_TEST_NAME,
 319         SET_ERR,
 320         SET_OUT,
 321         //SET_SUITE_NAME,
 322         SET_BASEDIR,
 323         SET_ABSOLUTE_DIR,
 324         RUN_TEST;
 325     }
 326         final String param;
 327         final CommandType type;
 328 
 329         @Override
 330         public String toString() {
 331             return "TestRunnerCommand{type='" + type + "',param='" + param + "'}";
 332         }
 333 
 334         public Command(CommandType cmd, String param) {
 335             this.param = param;
 336             this.type = cmd;
 337         }
 338     }
 339     
 340 }