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 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.ImagesManager;
  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                         //TODO: this design is a bit flawed as we introducing backward
 206                         //dependency on SharedTestUtils here.
 207                         ImagesManager.getInstance().setBaseDir(trc.param);
 208                         break;
 209                     case SET_ABSOLUTE_DIR:
 210                         ImagesManager.getInstance().setAbsoluteDir(trc.param);
 211                         break;
 212                     case SET_TEST_NAME:
 213                         testName = trc.param;
 214                         break;
 215                     case SET_ERR:
 216                         System.setErr(new PrintStream(new File(trc.param)));
 217                         break;
 218                     case SET_OUT:
 219                         System.setOut(new PrintStream(new File(trc.param)));
 220                         break;
 221                     case RUN_TEST:
 222                         if (testClassName == null) {
 223                         handleEx(new IllegalStateException("Running test without classname!"));
 224                         break loop;
 225                     } else {
 226                         runTest();
 227                     }
 228                         break;
 229                     case CHECK_UI_AND_EXIT:
 230                         try {
 231                         waitUI();
 232                         testRunner.checkUI();
 233                     } catch (Throwable ex) {
 234                         handleEx(ex);
 235                     }
 236                         exit(testRunner.getCurrentStatus());
 237                         break;
 238                     case EXIT:
 239                         // we want be able to wait for abort here
 240                         if (!exitThread.isAlive()) {
 241                         exitThread.start();
 242                     }
 243                         break;
 244                     case ABORT:
 245                         exit(Abstract2TestRunner.Status.ABORTED);
 246                         break;
 247                     default:
 248                         exit(Abstract2TestRunner.Status.UKNOWN_COMMAND);
 249                         break;
 250                 }
 251 
 252             } while (true);
 253             readerStream.close();
 254         } catch (Throwable ex) {
 255             if (!isExiting) {
 256                 handleEx(ex);
 257             }
 258         }
 259     }
 260 
 261     private void runTest() {
 262         testStart = System.nanoTime();
 263         new Thread(new Runnable() {
 264             @Override
 265             public void run() {
 266                 report("CommonTestRunnerWorker.runTest(): starting UI ...");
 267                 try {
 268                     testRunner.runUI();
 269                 } catch (Throwable ex) {
 270                     handleEx(ex);
 271                 } finally {
 272                     report("UI started");
 273                     uilock.release(1000);
 274                 }
 275             }
 276         }).start();
 277     }
 278 
 279     /**
 280      *
 281      * @param exitStatus
 282      */
 283     protected synchronized void exit(Abstract2TestRunner.Status exitStatus) {
 284         Abstract2TestRunner.Status exitValue = exitStatus;
 285         System.err.println("called exit " + exitValue);
 286 
 287         try {
 288             System.err.println("call stopui");
 289             testRunner.stopUI();
 290         } catch (Throwable ex) {
 291             ex.printStackTrace();
 292             exitValue = Abstract2TestRunner.Status.ERROR;
 293         }
 294 
 295         report("Exiting with status: " + exitValue);
 296         isExiting = true;
 297         if (socket != null && socket.isConnected()) {
 298             try {
 299                 new ObjectOutputStream(socket.getOutputStream()).writeObject(exitValue);
 300             } catch (IOException ex) {
 301                 ex.printStackTrace();
 302             }
 303         }
 304         System.exit(exitValue.getN());
 305     }
 306 
 307     static class Command implements Serializable {
 308     static enum CommandType {
 309         CHECK_UI_AND_EXIT,
 310         ABORT,
 311         STOP_UI,
 312         EXIT,
 313         SET_TEST_CLASS,
 314         SET_TEST_NAME,
 315         SET_ERR,
 316         SET_OUT,
 317         //SET_SUITE_NAME,
 318         SET_BASEDIR,
 319         SET_ABSOLUTE_DIR,
 320         RUN_TEST;
 321     }
 322         final String param;
 323         final CommandType type;
 324 
 325         @Override
 326         public String toString() {
 327             return "TestRunnerCommand{type='" + type + "',param='" + param + "'}";
 328         }
 329 
 330         public Command(CommandType cmd, String param) {
 331             this.param = param;
 332             this.type = cmd;
 333         }
 334     }
 335     
 336 }