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 }