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 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     /**
  81      *
  82      * @throws Throwable
  83      */
  84     public void runUI() throws Throwable {
  85         System.out.println("CommonTestRunnerWorker.runUI(): starting test...");
  86         core.addListener(new RunListener() {
  87 
  88             @Override
  89             public void testAssumptionFailure(Failure failure) {
  90                 System.out.println("junit failure: " + failure.getMessage());
  91                 failure.getException().printStackTrace();
  92                 exit(Abstract2TestRunner.Status.FAIL);
  93             }
  94 
  95             @Override
  96             public void testFailure(Failure failure) throws Exception {
  97                 System.out.println("junit failure: " + failure.getMessage());
  98                 failure.getException().printStackTrace();
  99                 exit(Abstract2TestRunner.Status.FAIL);
 100             }
 101 
 102         });
 103         Result r = core.run(Request.method(Class.forName(testClassName), testName));
 104         System.out.println("got result = " + r.wasSuccessful());
 105         exit(r.wasSuccessful() ? Abstract2TestRunner.Status.PASS : Abstract2TestRunner.Status.FAIL);
 106     }
 107 
 108     private void waitUI() {
 109         if (!uilock.tryAcquire()) {
 110             report("Waiting for UI to start or nodesc/auto test to finish");
 111             try {
 112                 uilock.acquire();
 113             } catch (InterruptedException ignored) {
 114                 ignored.printStackTrace();
 115             }
 116         }
 117         report("UI started successfully");
 118     }
 119 
 120     /**
 121      *
 122      * @param text
 123      */
 124     protected void report(String text) {
 125         long time = System.nanoTime() - testStart;
 126         System.out.printf("%dms: %s\n", time / 1000000, text);
 127     }
 128 
 129     private void handleEx(Throwable ex) {
 130         ex.printStackTrace();
 131         exit(Abstract2TestRunner.Status.ERROR);
 132     }
 133 
 134     /**
 135      *
 136      * @param args
 137      */
 138     protected final void setArgs(final String... args) {
 139         if (args != null && args.length > 0) {
 140             if (args.length == 1) {
 141                 testClassName = args[0];
 142             } else if (args.length >= 3 && args[0].equals("--debug")) {
 143                 System.err.println("debug mode");
 144                 debugMode = true;
 145                 testClassName = args[1];
 146                 testName = args[2];
 147             } else {
 148                 System.err.println("remote mode");
 149                 testClassName = args[0];
 150                 host = args[1];
 151             }
 152         }
 153         if (null == testClassName) {
 154             System.err.println("null == testClassName, argslen=" + args.length);
 155         }
 156     }
 157     
 158     /**
 159      *
 160      * @return
 161      */
 162     public String getTestClassName() {
 163         return testClassName;
 164     }
 165 
 166     /**
 167      *
 168      * @return
 169      */
 170     public String getTestName() {
 171         return testName;
 172     }
 173 
 174     /**
 175      *
 176      */
 177     protected final void prepareAndRunTest() {
 178 
 179         if (debugMode) {
 180             runTest();
 181             System.err.println("Test done.");
 182             return;
 183         }
 184 
 185         testStart = System.nanoTime();
 186         try {
 187             report("Looking for server at host " + host + ", port " + PORT + "...");
 188             socket = new Socket(host, PORT);
 189             ObjectInputStream readerStream = new ObjectInputStream(socket.getInputStream());
 190             report("Connected");
 191 
 192             loop:
 193             do {
 194                 Command trc = (Command) readerStream.readObject();
 195                 report("Got command: " + trc.type + ":" + trc.param);
 196                 switch (trc.type) {
 197                     case SET_TEST_CLASS:
 198                         testClassName = trc.param;
 199                         break;
 200                     case SET_BASEDIR:
 201                         //TODO: this design is a bit flawed as we introducing backward
 202                         //dependency on SharedTestUtils here.
 203                         ImagesManager.getInstance().setBaseDir(trc.param);
 204                         break;
 205                     case SET_ABSOLUTE_DIR:
 206                         ImagesManager.getInstance().setAbsoluteDir(trc.param);
 207                         break;
 208                     case SET_TEST_NAME:
 209                         testName = trc.param;
 210                         break;
 211                     case SET_ERR:
 212                         System.setErr(new PrintStream(new File(trc.param)));
 213                         break;
 214                     case SET_OUT:
 215                         System.setOut(new PrintStream(new File(trc.param)));
 216                         break;
 217                     case RUN_TEST:
 218                         if (testClassName == null) {
 219                         handleEx(new IllegalStateException("Running test without classname!"));
 220                         break loop;
 221                     } else {
 222                         runTest();
 223                     }
 224                         break;
 225                     case CHECK_UI_AND_EXIT:
 226                         try {
 227                         waitUI();
 228                         testRunner.checkUI();
 229                     } catch (Throwable ex) {
 230                         handleEx(ex);
 231                     }
 232                         exit(testRunner.getCurrentStatus());
 233                         break;
 234                     case EXIT:
 235                         // we want be able to wait for abort here
 236                         if (!exitThread.isAlive()) {
 237                         exitThread.start();
 238                     }
 239                         break;
 240                     case ABORT:
 241                         exit(Abstract2TestRunner.Status.ABORTED);
 242                         break;
 243                     default:
 244                         exit(Abstract2TestRunner.Status.UKNOWN_COMMAND);
 245                         break;
 246                 }
 247 
 248             } while (true);
 249             readerStream.close();
 250         } catch (Throwable ex) {
 251             if (!isExiting) {
 252                 handleEx(ex);
 253             }
 254         }
 255     }
 256 
 257     private void runTest() {
 258         testStart = System.nanoTime();
 259         new Thread(new Runnable() {
 260             @Override
 261             public void run() {
 262                 report("CommonTestRunnerWorker.runTest(): starting UI ...");
 263                 try {
 264                     testRunner.runUI();
 265                 } catch (Throwable ex) {
 266                     handleEx(ex);
 267                 } finally {
 268                     report("UI started");
 269                     uilock.release(1000);
 270                 }
 271             }
 272         }).start();
 273     }
 274 
 275     /**
 276      *
 277      * @param exitStatus
 278      */
 279     protected synchronized void exit(Abstract2TestRunner.Status exitStatus) {
 280         Abstract2TestRunner.Status exitValue = exitStatus;
 281         System.err.println("called exit " + exitValue);
 282 
 283         try {
 284             System.err.println("call stopui");
 285             testRunner.stopUI();
 286         } catch (Throwable ex) {
 287             ex.printStackTrace();
 288             exitValue = Abstract2TestRunner.Status.ERROR;
 289         }
 290 
 291         report("Exiting with status: " + exitValue);
 292         isExiting = true;
 293         if (socket != null && socket.isConnected()) {
 294             try {
 295                 new ObjectOutputStream(socket.getOutputStream()).writeObject(exitValue);
 296             } catch (IOException ex) {
 297                 ex.printStackTrace();
 298             }
 299         }
 300         System.exit(exitValue.getN());
 301     }
 302 
 303     static class Command implements Serializable {
 304     static enum CommandType {
 305         CHECK_UI_AND_EXIT,
 306         ABORT,
 307         STOP_UI,
 308         EXIT,
 309         SET_TEST_CLASS,
 310         SET_TEST_NAME,
 311         SET_ERR,
 312         SET_OUT,
 313         //SET_SUITE_NAME,
 314         SET_BASEDIR,
 315         SET_ABSOLUTE_DIR,
 316         RUN_TEST;
 317     }
 318         final String param;
 319         final CommandType type;
 320 
 321         @Override
 322         public String toString() {
 323             return "TestRunnerCommand{type='" + type + "',param='" + param + "'}";
 324         }
 325 
 326         public Command(CommandType cmd, String param) {
 327             this.param = param;
 328             this.type = cmd;
 329         }
 330     }
 331     
 332 }