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 }