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 }