1 /* 2 * $Id$ 3 * 4 * Copyright (c) 2004, 2009, Oracle and/or its affiliates. All rights reserved. 5 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 6 * 7 * This code is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License version 2 only, as 9 * published by the Free Software Foundation. Oracle designates this 10 * particular file as subject to the "Classpath" exception as provided 11 * by Oracle in the LICENSE file that accompanied this code. 12 * 13 * This code is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16 * version 2 for more details (a copy is included in the LICENSE file that 17 * accompanied this code). 18 * 19 * You should have received a copy of the GNU General Public License version 20 * 2 along with this work; if not, write to the Free Software Foundation, 21 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 22 * 23 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 24 * or visit www.oracle.com if you need additional information or have any 25 * questions. 26 */ 27 package com.sun.javatest; 28 29 import java.io.PrintWriter; 30 import java.util.HashSet; 31 import java.util.Iterator; 32 import java.util.Set; 33 34 import com.sun.javatest.util.BackupPolicy; 35 import com.sun.javatest.util.I18NResourceBundle; 36 37 /** 38 * Traditional implementation of the test execution engine which has been 39 * used throughout the JT Harness 2.x harness. It supplies all the basic 40 * for creating threads for each test, running the <code>Script</code>, 41 * and handling timeouts. 42 */ 43 public class DefaultTestRunner extends TestRunner 44 { 45 public synchronized boolean runTests(Iterator<TestDescription> testIter) 46 throws InterruptedException 47 { 48 this.testIter = testIter; 49 50 Thread[] threads = new Thread[getConcurrency()]; 51 activeThreads = new HashSet<>(); 52 allPassed = true; 53 54 try { 55 int n = 0; 56 while (!stopping) { 57 for (int i = 0; i < threads.length; i++) { 58 Thread t = threads[i]; 59 if (t == null || !activeThreads.contains(t)) { 60 int prio = Math.max(Thread.MIN_PRIORITY, Thread.currentThread().getPriority() - 1); 61 t = new Thread() { 62 public void run() { 63 try { 64 TestDescription td; 65 while ((td = nextTest()) != null) { 66 if (!runTest(td)) 67 allPassed = false; 68 } 69 } 70 finally { 71 // Inform runner this thread is dying, so it can start another thread 72 // to replace it, if necessary. 73 threadExiting(this); 74 } 75 } 76 }; 77 t.setName("DefaultTestRunner:Worker-" + i + ":" + n++); 78 t.start(); 79 t.setPriority(prio); 80 activeThreads.add(t); 81 threads[i] = t; 82 } 83 } 84 wait(); 85 } 86 // Wait for all the threads to finish so they don't get nuked by the 87 // finally code. Order is not important so just wait for them one at a time. 88 // Note we can't simply join with the thread because that gives a deadlock 89 // on our lock. 90 for (int i = 0; i < threads.length; i++) { 91 if (threads[i] != null) { 92 while (activeThreads.contains(threads[i])) 93 wait(); 94 threads[i] = null; 95 } 96 } 97 } 98 catch (InterruptedException ex) { 99 // The thread has been interrupted 100 101 stopping = true; // stop workers from starting any new tests 102 103 // interrupt the worker threads 104 for (Thread t : activeThreads) { 105 t.interrupt(); 106 } 107 108 // while a short while (a couple of seconds) for tests to clean up 109 // before we nuke them 110 long now = System.currentTimeMillis(); 111 try { 112 while (activeThreads.size() > 0 && (System.currentTimeMillis() - now < 2000)) { 113 wait(100); 114 } 115 } 116 catch (InterruptedException e) { 117 } 118 119 // rethrow the original exception so the caller knows what's happened 120 throw ex; 121 } 122 finally { 123 // ensure all child threads killed 124 for (int i = 0; i < threads.length; i++) { 125 if (threads[i] != null) 126 Deprecated.invokeThreadStop(threads[i]); 127 } 128 } 129 130 return allPassed; 131 } 132 133 private synchronized void threadExiting(Thread t) { 134 activeThreads.remove(t); 135 notifyAll(); 136 } 137 138 private synchronized TestDescription nextTest() { 139 if (stopping) 140 return null; 141 142 if (testIter.hasNext()) 143 return (testIter.next()); 144 else { 145 stopping = true; 146 return null; 147 } 148 } 149 150 private boolean runTest(TestDescription td) { 151 WorkDirectory workDir = getWorkDirectory(); 152 TestResult result = null; 153 154 boolean scriptUsesNotifier = false; 155 156 try { 157 TestSuite testSuite = getTestSuite(); 158 TestEnvironment env = getEnvironment(); 159 BackupPolicy backupPolicy = getBackupPolicy(); 160 161 String[] exclTestCases = getExcludedTestCases(td); 162 Script s = testSuite.createScript(td, exclTestCases, env.copy(), workDir, backupPolicy); 163 164 scriptUsesNotifier = s.useNotifier(); 165 if (!scriptUsesNotifier) { 166 notifyStartingTest(s.getTestResult()); 167 } else { 168 delegateNotifier(s); 169 } 170 171 result = s.getTestResult(); 172 173 s.run(); 174 } 175 catch (ThreadDeath e) { 176 String url = td.getRootRelativeURL(); 177 workDir.log(i18n, "dtr.threadKilled", url); 178 result = createErrorResult(td, i18n.getString("dtr.threadKilled", url), e); 179 throw e; 180 } 181 catch (Throwable e) { 182 String url = td.getRootRelativeURL(); 183 workDir.log(i18n, "dtr.unexpectedThrowable", 184 new Object[] { url, e, classifyThrowable(e) }); 185 result = createErrorResult(td, 186 i18n.getString("dtr.unexpectedThrowable", 187 new Object[] { url, e, classifyThrowable(e) }), 188 e); 189 } 190 finally { 191 if (result == null) { 192 String url = td.getRootRelativeURL(); 193 result = createErrorResult(td, i18n.getString("dtr.noResult", url), null); 194 } 195 196 if (!scriptUsesNotifier) { 197 try { 198 notifyFinishedTest(result); 199 } 200 catch (ThreadDeath e) { 201 String url = td.getRootRelativeURL(); 202 workDir.log(i18n, "dtr.threadKilled", url); 203 throw e; 204 } 205 catch (Throwable e) { 206 String url = td.getRootRelativeURL(); 207 workDir.log(i18n, "dtr.unexpectedThrowable", new Object[] { url, e, classifyThrowable(e) }); 208 } 209 } 210 } 211 212 return (result.getStatus().getType() == Status.PASSED); 213 } 214 215 private TestResult createErrorResult(TestDescription td, String reason, Throwable t) { // make more i18n 216 Status s = Status.error(reason); 217 TestResult tr; 218 if (t == null) 219 tr = new TestResult(td, s); 220 else { 221 tr = new TestResult(td); 222 TestResult.Section trs = tr.createSection(i18n.getString("dtr.details")); 223 PrintWriter pw = trs.createOutput(i18n.getString("dtr.stackTrace")); 224 t.printStackTrace(pw); 225 pw.close(); 226 tr.setStatus(s); 227 } 228 229 WorkDirectory workDir = getWorkDirectory(); 230 BackupPolicy backupPolicy = getBackupPolicy(); 231 try { 232 tr.writeResults(workDir, backupPolicy); 233 } 234 catch (Exception e) { 235 workDir.log(i18n, "dtr.unexpectedThrowable", 236 new Object[] {td.getRootRelativeURL(), e, EXCEPTION }); 237 } 238 return tr; 239 } 240 241 private Integer classifyThrowable(Throwable t) { 242 if (t instanceof Exception) 243 return EXCEPTION; 244 else if (t instanceof Error) 245 return ERROR; 246 else 247 return THROWABLE; 248 } 249 250 // constants used by classifyThrowable and i18n key unexpectedThrowable 251 private static final Integer EXCEPTION = new Integer(0); 252 private static final Integer ERROR = new Integer(1); 253 private static final Integer THROWABLE = new Integer(2); 254 255 256 private Iterator<TestDescription> testIter; 257 private Set<Thread> activeThreads; 258 private boolean allPassed; 259 private boolean stopping; 260 261 private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(DefaultTestRunner.class); 262 }