1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 2001, 2011, 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.exec;
  28 
  29 import java.awt.EventQueue;
  30 import java.util.ArrayList;
  31 
  32 import com.sun.javatest.Harness;
  33 import com.sun.javatest.Parameters;
  34 import com.sun.javatest.Status;
  35 import com.sun.javatest.TestResult;
  36 import com.sun.javatest.WorkDirectory;
  37 import com.sun.javatest.util.DynamicArray;
  38 
  39 /**
  40  * This class captures the state of the Harness for the purpose of
  41  * presenting the user with useful status/progress info.
  42  */
  43 
  44 class MonitorState {
  45     MonitorState(Harness h) {
  46         h.addObserver(dispatcher);
  47         harness = h;
  48         startTime = -1l;
  49         finishTime = -1l;
  50         stats = new int[Status.NUM_STATES];
  51     }
  52 
  53     /**
  54      * Monitor the harness state.
  55      * By definition, all observations will be broadcast on the event thread.
  56      * Be short and timely in processing these events.
  57      */
  58     interface Observer {
  59         /**
  60          * A new test run is starting.
  61          */
  62         public void starting();
  63 
  64         /**
  65          * The tests have stopped running, and the harness is now doing
  66          * cleanup.
  67          */
  68         public void postProcessing();
  69 
  70         /**
  71           * A test run is being stopped by something.  This is not the same
  72           * as finishing.
  73           */
  74         public void stopping();
  75 
  76         /**
  77          * A test run is finishing.
  78          *
  79          * @param allOk Did all the tests pass?
  80          */
  81         public void finished(boolean allOk);
  82     }
  83 
  84     void addObserver(Observer o) {
  85         obs = (Observer[])DynamicArray.append(obs, o);
  86     }
  87 
  88     void removeObserver(Observer o) {
  89         obs = (Observer[])DynamicArray.remove(obs, o);
  90     }
  91 
  92     /**
  93      * Is the harness currently doing a test run.
  94      */
  95     boolean isRunning() {
  96         return running;
  97     }
  98 
  99     /**
 100      * Time since the start of the run.
 101      *
 102      * @return Zero at the instant we start a run or when no run has been
 103      *         performed yet.
 104      */
 105     long getElapsedTime() {
 106         /*
 107         // we don't have a valid time available
 108         if (startTime == -1)
 109             return 0L;
 110 
 111         // provide the current or last time
 112         long now = System.currentTimeMillis();
 113 
 114         // answer depends whether we are running now or not
 115         if (finishTime < 0) {
 116             // we are still running
 117             return now - startTime;
 118         }
 119         else {
 120             return finishTime - startTime;
 121         }
 122         */
 123 
 124         return harness.getElapsedTime();
 125     }
 126 
 127     /**
 128      * Find out the estimated time required to complete the remaining tests.
 129      *
 130      * @return A time estimate in milliseconds.  Zero if no run is in progress or
 131      *         no estimate is available.
 132      */
 133     synchronized long getEstimatedTime() {
 134         return harness.getEstimatedTime();
 135     }
 136 
 137     /**
 138      * Discover the number of tests found so far in this testsuite.
 139      *
 140      * @return estimated number of tests in the entire testsuite, -1 if unknown
 141      */
 142     synchronized int getEstimatedTotalTests() {
 143         WorkDirectory wd = harness.getResultTable().getWorkDir();
 144         if (wd == null)
 145             return -1;
 146 
 147         int count = wd.getTestSuiteTestCount();
 148 
 149         if (count == -1)
 150             count = wd.getTestSuite().getEstimatedTestCount();
 151 
 152         // make sure we return -1
 153         return (count < 0 ? -1 : count);
 154     }
 155 
 156     /**
 157      * Discover the number of tests found so far by the system to be executed
 158      * during this run.
 159      *
 160      * @return Number of tests found so far to be executed based on the current
 161      *         parameters.
 162      */
 163     synchronized int getTestsFoundCount() {
 164         return harness.getTestsFoundCount();
 165     }
 166 
 167     /**
 168      * Convenience method to determine how many tests have finished for the
 169      * current or last run.
 170      */
 171     synchronized int getTestsDoneCount() {
 172         int done = 0;
 173         for (int i = 0; i < stats.length; i++)
 174             done += stats[i];
 175 
 176         return done;
 177     }
 178 
 179     /**
 180      * Convenience method to determine how many tests have still need
 181      * to be run for the current or last run.
 182      */
 183     synchronized int getTestsRemainingCount() {
 184         return getTestsFoundCount() - getTestsDoneCount();
 185     }
 186 
 187     /**
 188      * Get the test run statistics.
 189      *
 190      * @return array of counts, based on the indexes of Status types.
 191      */
 192     synchronized int[] getStats() {
 193         // we could consider trusting the client and not doing the copy
 194         int[] copy = new int[stats.length];
 195         System.arraycopy(stats, 0, copy, 0, stats.length);
 196         return copy;
 197     }
 198 
 199     /**
 200      * @return Array representing the currently executing tests. Zero-length
 201      *     array if there are no tests running.
 202      */
 203     TestResult[] getRunningTests() {
 204         TestResult[] trs = new TestResult[0];
 205         synchronized (vLock) {
 206             if (runningTests != null && runningTests.size() > 0)
 207                 trs = runningTests.toArray(trs);
 208         }
 209 
 210         return trs;
 211     }
 212 
 213     /**
 214      * Provide synchronized access to the statistics array.
 215      */
 216     private synchronized void incrementStat(int state) {
 217         // this mainly called by the harness observer implementation
 218         stats[state]++;
 219     }
 220 
 221     private synchronized void resetStats() {
 222         stats = new int[Status.NUM_STATES];
 223     }
 224 
 225     class Dispatcher implements Harness.Observer {
 226 
 227         public void startingTestRun(Parameters p) {
 228             running = true;
 229 
 230             startTime = System.currentTimeMillis();
 231             finishTime = -1l;
 232             resetStats();
 233 
 234             synchronized (vLock) {
 235                 runningTests.clear();
 236             }
 237 
 238             notifySimple(0);
 239         }
 240 
 241         public void startingTest(TestResult tr) {
 242             synchronized (vLock) {
 243                 if (!runningTests.contains(tr))
 244                     runningTests.add(tr);
 245             }
 246         }
 247 
 248         public void finishedTest(TestResult tr) {
 249             synchronized (vLock) {
 250                 runningTests.remove(tr);
 251             }
 252 
 253             incrementStat(tr.getStatus().getType());
 254         }
 255 
 256         public void stoppingTestRun() {
 257             notifySimple(1);
 258         }
 259 
 260         public void finishedTesting() {
 261             finishTime = System.currentTimeMillis();
 262 
 263             synchronized (vLock) {
 264                 runningTests.clear();
 265             }
 266 
 267             notifySimple(2);
 268         }
 269 
 270         public void finishedTestRun(boolean allOk) {
 271             running = false;
 272             notifyComplete(allOk);
 273         }
 274 
 275         public void error(String msg) {
 276         }
 277 
 278         // --------- private ----------
 279         private void notifySimple(final int which) {
 280             if (!EventQueue.isDispatchThread()) {
 281                 Runnable cmd = new Runnable() {
 282                     public void run() {
 283                         MonitorState.Dispatcher.this.notifySimple(which);
 284                     }   // run()
 285                 };      // end anon. class
 286 
 287                 EventQueue.invokeLater(cmd);
 288             }
 289             else {      // now on event thread
 290                 for (int i = 0; i < obs.length; i++)
 291                     switch (which) {
 292                         case 0:
 293                             obs[i].starting(); break;
 294                         case 1:
 295                             obs[i].stopping(); break;
 296                         case 2:
 297                             obs[i].postProcessing(); break;
 298                         default:
 299                             throw new IllegalStateException();
 300                     }   // switch
 301                 // end for
 302             }   // else
 303         }
 304 
 305         private void notifyComplete(final boolean allOk) {
 306             if (!EventQueue.isDispatchThread()) {
 307                 Runnable cmd = new Runnable() {
 308                     public void run() {
 309                         MonitorState.Dispatcher.this.notifyComplete(allOk);
 310                     }   // run()
 311                 };      // end anon. class
 312 
 313                 EventQueue.invokeLater(cmd);
 314             }
 315             else {      // now on event thread
 316                 for (int i = 0; i < obs.length; i++)
 317                     obs[i].finished(allOk);
 318             }
 319         }
 320     }
 321 
 322     // instance vars for MonitorState
 323     private Harness harness;
 324     private boolean running;
 325     private Observer[] obs = new Observer[0];
 326     private volatile int[] stats;
 327 
 328     private ArrayList<TestResult> runningTests = new ArrayList(5);
 329     private final Object vLock = new Object();
 330 
 331     /**
 332      * Basis for the elapsed time.
 333      */
 334     private long startTime;
 335     private long finishTime;    // only used if startTime != -1 and finishTime >= 0
 336     private Dispatcher dispatcher = new Dispatcher();
 337 }
 338