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 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 (Iterator iter = activeThreads.iterator() ; iter.hasNext(); ) { 105 Thread t = (Thread) (iter.next()); 106 t.interrupt(); 107 } 108 109 // while a short while (a couple of seconds) for tests to clean up 110 // before we nuke them 111 long now = System.currentTimeMillis(); 112 try { 113 while (activeThreads.size() > 0 && (System.currentTimeMillis() - now < 2000)) { 114 wait(100); 115 } 116 } 117 catch (InterruptedException e) { 118 } 119 120 // rethrow the original exception so the caller knows what's happened 121 throw ex; 122 } 123 finally { 124 // ensure all child threads killed 125 for (int i = 0; i < threads.length; i++) { 126 if (threads[i] != null) 127 Deprecated.invokeThreadStop(threads[i]); 128 } 129 } 130 131 return allPassed; 132 } 133 134 private synchronized void threadExiting(Thread t) { 135 activeThreads.remove(t); 136 notifyAll(); 137 } 138 139 private synchronized TestDescription nextTest() { 140 if (stopping) 141 return null; 142 143 if (testIter.hasNext()) 144 return (TestDescription) (testIter.next()); 145 else { 146 stopping = true; 147 return null; 148 } 149 } 150 151 private boolean runTest(TestDescription td) { 152 WorkDirectory workDir = getWorkDirectory(); 153 TestResult result = null; 154 155 boolean scriptUsesNotifier = false; 156 157 try { 158 TestSuite testSuite = getTestSuite(); 159 TestEnvironment env = getEnvironment(); 160 BackupPolicy backupPolicy = getBackupPolicy(); 161 162 String[] exclTestCases = getExcludedTestCases(td); 163 Script s = testSuite.createScript(td, exclTestCases, env.copy(), workDir, backupPolicy); 164 165 scriptUsesNotifier = s.useNotifier(); 166 if (!scriptUsesNotifier) { 167 notifyStartingTest(s.getTestResult()); 168 } else { 169 delegateNotifier(s); 170 } 171 172 result = s.getTestResult(); 173 174 s.run(); 175 } 176 catch (ThreadDeath e) { 177 String url = td.getRootRelativeURL(); 178 workDir.log(i18n, "dtr.threadKilled", url); 179 result = createErrorResult(td, i18n.getString("dtr.threadKilled", url), e); 180 throw e; 181 } 182 catch (Throwable e) { 183 String url = td.getRootRelativeURL(); 184 workDir.log(i18n, "dtr.unexpectedThrowable", 185 new Object[] { url, e, classifyThrowable(e) }); 186 result = createErrorResult(td, 187 i18n.getString("dtr.unexpectedThrowable", 188 new Object[] { url, e, classifyThrowable(e) }), 189 e); 190 } 191 finally { 192 if (result == null) { 193 String url = td.getRootRelativeURL(); 194 result = createErrorResult(td, i18n.getString("dtr.noResult", url), null); 195 } 196 197 if (!scriptUsesNotifier) { 198 try { 199 notifyFinishedTest(result); 200 } 201 catch (ThreadDeath e) { 202 String url = td.getRootRelativeURL(); 203 workDir.log(i18n, "dtr.threadKilled", url); 204 throw e; 205 } 206 catch (Throwable e) { 207 String url = td.getRootRelativeURL(); 208 workDir.log(i18n, "dtr.unexpectedThrowable", new Object[] { url, e, classifyThrowable(e) }); 209 } 210 } 211 } 212 213 return (result.getStatus().getType() == Status.PASSED); 214 } 215 216 private TestResult createErrorResult(TestDescription td, String reason, Throwable t) { // make more i18n 217 Status s = Status.error(reason); 218 TestResult tr; 219 if (t == null) 220 tr = new TestResult(td, s); 221 else { 222 tr = new TestResult(td); 223 TestResult.Section trs = tr.createSection(i18n.getString("dtr.details")); 224 PrintWriter pw = trs.createOutput(i18n.getString("dtr.stackTrace")); 225 t.printStackTrace(pw); 226 pw.close(); 227 tr.setStatus(s); 228 } 229 230 WorkDirectory workDir = getWorkDirectory(); 231 BackupPolicy backupPolicy = getBackupPolicy(); 232 try { 233 tr.writeResults(workDir, backupPolicy); 234 } 235 catch (Exception e) { 236 workDir.log(i18n, "dtr.unexpectedThrowable", 237 new Object[] {td.getRootRelativeURL(), e, EXCEPTION }); 238 } 239 return tr; 240 } 241 242 private Integer classifyThrowable(Throwable t) { 243 if (t instanceof Exception) 244 return EXCEPTION; 245 else if (t instanceof Error) 246 return ERROR; 247 else 248 return THROWABLE; 249 } 250 251 // constants used by classifyThrowable and i18n key unexpectedThrowable 252 private static final Integer EXCEPTION = new Integer(0); 253 private static final Integer ERROR = new Integer(1); 254 private static final Integer THROWABLE = new Integer(2); 255 256 257 private Iterator testIter; 258 private Set<Thread> activeThreads; 259 private boolean allPassed; 260 private boolean stopping; 261 262 private static I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(DefaultTestRunner.class); 263 }