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 }