1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 1996, 2014, 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.BufferedReader;
  30 import java.io.File;
  31 import java.io.FileReader;
  32 import java.io.IOException;
  33 import java.io.PrintStream;
  34 import java.io.PrintWriter;
  35 import java.io.StreamTokenizer;
  36 import java.util.Date;
  37 import java.util.Vector;
  38 
  39 import com.sun.javatest.util.BackupPolicy;
  40 import com.sun.javatest.util.I18NResourceBundle;
  41 import com.sun.javatest.util.StringArray;
  42 import com.sun.javatest.util.Timer;
  43 
  44 /**
  45  * Script is the abstract base class providing the ability to control
  46  * how a test is to be compiled and executed. In addition to the primary method,
  47  * <em>run</em>, it has many methods that can be used by subtype classes
  48  * to assist them in performing a test.
  49  */
  50 public abstract class Script
  51 {
  52     /**
  53      * Initialize any custom args for the script.
  54      * @param args custom args for the script
  55      */
  56     public void initArgs(String[] args) {
  57         scriptArgs = args;
  58     }
  59 
  60     /**
  61      * Initialize the test description to be run by the script.
  62      * In addition, a mutable test result is set up, in which the results of running
  63      * the test can be recorded by the script.
  64      * @param td the test description for the test to be run
  65      */
  66     public void initTestDescription(TestDescription td) {
  67         this.td = td;
  68         testResult = new TestResult(td);
  69         trOut = testResult.getTestCommentWriter();
  70     }
  71 
  72     /**
  73      * Initialize the list of test cases to be excluded from the test.
  74      * The script is responsible for determining how to instruct the test
  75      * not to run these test cases. A recommended convention is to pass the
  76      * list of test cases to the test using a -exclude option.
  77      * @param excludedTestCases a list of test cases within the test that
  78      * should not be run
  79      */
  80     public void initExcludedTestCases(String[] excludedTestCases) {
  81         this.excludedTestCases = excludedTestCases;
  82     }
  83 
  84     /**
  85      * Initialize the environment to be used when running the test.
  86      * @param env the environment to be used when running the test
  87      */
  88     public void initTestEnvironment(TestEnvironment env) {
  89         this.env = env;
  90     }
  91 
  92     /**
  93      * Initialize the work directory to be used to store the results
  94      * obtained when running the test,
  95      * and to store any temporary files that may be required by the test.
  96      * @param workDir the work directory to be used to store the test's results.
  97      */
  98     public void initWorkDir(WorkDirectory workDir) {
  99         this.workDir = workDir;
 100     }
 101 
 102     /**
 103      * Initialize the backup policy to be used when creating a test result
 104      * file in which to store the results of running this test.
 105      * @param backupPolicy A backup policy object to be used when
 106      * creating test result files.
 107      */
 108     public void initBackupPolicy(BackupPolicy backupPolicy) {
 109         this.backupPolicy = backupPolicy;
 110     }
 111 
 112     /**
 113      * Initialize the class loader for any commands to be loaded.
 114      * @param loader a class loader to be used to load any commands or other
 115      * user-specified classes that may be required.
 116      */
 117     public void initClassLoader(ClassLoader loader) {
 118         this.loader = loader;
 119     }
 120 
 121     /**
 122      * Initialize a delegate script object. This should only be used in
 123      * exceptional circumstances, and is mostly provided for historical purposes.
 124      * @param s The delegate to be initialized
 125      * @param scriptArgs the arguments to be passed to the delegate object
 126      */
 127     protected void initDelegate(Script s, String[] scriptArgs) {
 128         s.scriptArgs = scriptArgs;
 129         // copy rest of values across from self
 130         s.td = td;
 131         s.env = env;
 132         s.workDir = workDir;
 133         s.backupPolicy = backupPolicy;
 134         s.loader = loader;
 135         s.testResult = testResult;
 136         s.trOut = trOut;
 137         s.jtrIfPassed = jtrIfPassed;
 138     }
 139 
 140     /**
 141      * Initialize the test result for the result of the script execution.
 142      * Normally, a test result is initialized as a side effect of calling
 143      * initTestDescription. This method should only be called is special
 144      * circumstances, and is mostly provided for historical purposes.
 145      * @param tr The test result to set as the result of the script's execution.
 146      * @throws IllegalStateException if the test result has already been set.
 147      * @see #initTestDescription
 148      */
 149     protected void initTestResult(TestResult tr) {
 150         if (testResult != null)
 151             throw new IllegalStateException();
 152 
 153         testResult = tr;
 154     }
 155 
 156     /**
 157      * Run the script, to fill out the test results for the test description
 158      * given to <code>init</code>. Most implementations will use the default
 159      * implementation of this method, which delegates to a simpler (abstract)
 160      * method @link(run(String[],TestDescription, TestEnvironment)).  If you
 161      * override this method, be aware that this method does insert many of the
 162      * standard result properties into the TestResult object - harness info,
 163      * start stop times, etc.
 164      */
 165     public void run() {
 166         if (workDir == null)
 167             throw new NullPointerException(i18n.getString("script.noWorkDir"));
 168         if (td == null)
 169             throw new NullPointerException(i18n.getString("script.noTestDesc"));
 170         if (testResult == null)
 171             throw new NullPointerException(i18n.getString("script.noTestRslt"));
 172         if (env == null)
 173             throw new NullPointerException(i18n.getString("script.noTestEnv"));
 174 
 175         Status execStatus = null;
 176 
 177         // "work" has the the work dir for the suite
 178         // "testWork" has the work dir for this test
 179         File testWork = workDir.getFile(td.getRootRelativeDir().getPath());
 180 
 181         // synchronize against interference by other scripts
 182         synchronized (Script.class) {
 183             if (!testWork.exists()) {
 184                 testWork.mkdirs();
 185             }
 186         }
 187 
 188         long startMs = System.currentTimeMillis();
 189 
 190         String descUrl = td.getFile().toURI().toASCIIString();
 191         String id = td.getId();
 192         if (id != null)
 193             descUrl += "#" + id;
 194         testResult.putProperty(TestResult.DESCRIPTION, descUrl);
 195         testResult.putProperty(TestResult.START, testResult.formatDate(new Date()));
 196         testResult.putProperty(TestResult.VERSION, ProductInfo.getVersion());
 197         testResult.putProperty(TestResult.WORK, testWork.getAbsolutePath());
 198         testResult.putProperty(TestResult.ENVIRONMENT, env.getName());
 199         testResult.putProperty(TestResult.VARIETY, ProductInfo.getHarnessVariety());
 200         testResult.putProperty(TestResult.LOADER, ProductInfo.getPackagingType());
 201 
 202         if (osInfo == null) {
 203             String osArch = System.getProperty("os.arch");
 204             String osName = System.getProperty("os.name");
 205             String osVersion = System.getProperty("os.version");
 206             osInfo = osName + " " + osVersion + " (" + osArch + ")";
 207         }
 208         testResult.putProperty(TestResult.JAVATEST_OS, osInfo);
 209         if (excludedTestCases != null)
 210             testResult.putProperty("excludedTestCases", StringArray.join(excludedTestCases));
 211 
 212         String classDir = td.getParameter("classDir");
 213         File f = (classDir == null ? workDir.getFile(defaultClassDir) :
 214                  new File(testWork, classDir));
 215         env.putUrlAndFile("testClassDir", f);
 216         env.putUrlAndFile("testWorkDir", testWork);
 217         env.put("test", td.getFile().getPath());
 218         env.put("testDir", td.getFile().getParent());
 219         env.put("testURL", descUrl);
 220         env.put("testPath", td.getRootRelativeURL());
 221 
 222         int timeout = getTestTimeout();
 223         PrintStream out = System.out;
 224         PrintStream err = System.err;
 225 
 226         try {
 227             testResult.putProperty(TestResult.TEST, td.getRootRelativeURL());
 228             StringBuilder sb = new StringBuilder(this.getClass().getName());
 229             String args = StringArray.join(scriptArgs);
 230             if (args != null && args.length() > 0) {
 231                 sb.append(" ");
 232                 sb.append(args);
 233             }
 234             testResult.putProperty(TestResult.SCRIPT, sb.toString());
 235 
 236             if (timeout > 0) {
 237                 testResult.putProperty("timeoutSeconds", Integer.toString(timeout));
 238                 setAlarm(timeout*1000);
 239             }
 240 
 241             execStatus = run(scriptArgs, td, env);
 242         }
 243         finally {
 244             if (timeout > 0)
 245                 setAlarm(0);
 246 
 247             try {
 248                 System.setOut(System.out);
 249                 System.setErr(System.err);
 250             }
 251             catch (SecurityException ignore) {
 252             }
 253 
 254             if (Thread.interrupted()) // will clear interrupted status of thread, as desired
 255                 execStatus = Status.error(i18n.getString("script.interrupted"));
 256 
 257             testResult.putProperty(TestResult.END,
 258                     testResult.formatDate(new Date()));
 259 
 260             if (execStatus == null) {
 261                 execStatus = Status.error(i18n.getString("script.noStatus"));
 262             }
 263             else {
 264                 switch (execStatus.getType()) {
 265                 case Status.PASSED:
 266                 case Status.FAILED:
 267                 case Status.ERROR:
 268                     break;
 269                 default:
 270                     execStatus = Status.error(i18n.getString("script.badTestStatus", execStatus));
 271                 }
 272             }
 273 
 274         }
 275 
 276         testResult.setEnvironment(env);
 277         testResult.putProperty("totalTime", Long.toString(System.currentTimeMillis() - startMs));
 278         testResult.setStatus(execStatus);
 279 
 280         try {
 281             if (execStatus.getType() != Status.PASSED || jtrIfPassed)
 282                 testResult.writeResults(workDir, backupPolicy);
 283         }
 284         catch (IOException e) {
 285             // ignore it; the test will have an error status already
 286             //throw new JavaTestError("Unable to write result file! " + e);
 287         }
 288     }
 289 
 290     /**
 291      * The primary method to be provided by Scripts. It is responsible for compiling
 292      * and executing the test appropiately.  Normally, a script should call `init' and
 293      * then decode any script-specific options it is given in `args'. It should then
 294      * examine the test description it is given so that it can compile and execute
 295      * the test as appropriate. Various convenience routines are provided to
 296      * simplify the task of running the compiler, an interpreter or any other commands,
 297      * which can be specified in a flexible manner by properties in the TestEnvironment.
 298      *
 299      * @param args      Any script-specific options specified in the script property
 300      * @param td        The test description for the test to be performed
 301      * @param env       The test environment giving the details of how to run the test
 302      * @return          The result of running the script
 303      * @see #compileIndividually
 304      * @see #compileTogether
 305      * @see #execute
 306      * @see #invokeCommand
 307      */
 308     public abstract Status run(String[] args, TestDescription td, TestEnvironment env);
 309 
 310     /**
 311      * Get the test description for the test which this script will run.
 312      * @return the test description for the test which this script will run.
 313      */
 314     public TestDescription getTestDescription() {
 315         return td;
 316     }
 317 
 318     /**
 319      * Get the test result object to be used for the results of the test run.
 320      * @return the test result object to be used for the results of the test run.
 321      */
 322     public TestResult getTestResult() {
 323         return testResult;
 324     }
 325 
 326     /**
 327      * Get the flag that indicates whether a result (.jtr) file should be written
 328      * even if the test has passed. By default, this is true.
 329      * @return the flag that indicates whether a result (.jtr) file should be written
 330      * even if the test has passed.
 331      * @see #setJTRIfPassed
 332      */
 333     public boolean getJTRIfPassed() {
 334         return jtrIfPassed;
 335     }
 336 
 337     /**
 338      * Set the flag that indicates whether a result (.jtr) file should be written
 339      * even if the test has passed. By default, this is true.
 340      * @param b the flag that indicates whether a result (.jtr) file should be written
 341      * even if the test has passed.
 342      * @see #getJTRIfPassed
 343      */
 344     public void setJTRIfPassed(boolean b) {
 345         jtrIfPassed = b;
 346     }
 347 
 348     /**
 349      * Set an alarm that will interrupt the calling thread after
 350      * a specified delay (in milliseconds), and repeatedly thereafter
 351      * until cancelled.
 352      * Typical usage:
 353      * <pre>
 354      * try {
 355      *     setAlarm(delay);
 356      *     ...
 357      * }
 358      * finally {
 359      *     setAlarm(0);
 360      * }
 361      * </pre>
 362      * @param timeout the interval (in milliseconds) after which the calling
 363      * thread will be interrupted, if not cancelled in the meantime.
 364      */
 365     protected void setAlarm(int timeout) {
 366         setAlarm(timeout, Thread.currentThread());
 367     }
 368 
 369 
 370     /**
 371      * Set an alarm that will interrupt a given thread after
 372      * a specified delay (in milliseconds), and repeatedly thereafter
 373      * until cancelled.
 374      * Typical usage:
 375      * <pre>
 376      * try {
 377      *     setAlarm(delay);
 378      *     ...
 379      * }
 380      * finally {
 381      *     setAlarm(0);
 382      * }
 383      * </pre>
 384      * @param timeout the interval (in milliseconds) after which the calling
 385      *      thread will be interrupted, if not cancelled in the meantime.
 386      * @param threadToInterrupt which thread to interrupt
 387      */
 388     protected void setAlarm(int timeout, Thread threadToInterrupt) {
 389         if (alarm != null) {
 390             alarm.cancel();
 391             alarm = null;
 392         }
 393 
 394         if (timeout > 0)
 395             alarm = new Alarm(timeout, threadToInterrupt);
 396     }
 397 
 398     /**
 399      * Set TimeoutProvider used to control test timeouts.
 400      *
 401      * @see TimeoutProvider
 402      * @see #getTestTimeout()
 403      * @see #getTimeoutProvider()
 404      * @param provider null to use default test timeout value (10 sec).
 405      */
 406     public void setTimeoutProvider(TimeoutProvider provider) {
 407         if(provider != this.provider) {
 408             this.provider = provider;
 409         }
 410     }
 411 
 412     /**
 413      * Getter for TimeoutProvider. Generates default (10*factor)
 414      * provider in case no provider is set
 415      *
 416      * @return TimeoutProvider set to Script. Returns default
 417      * TimeoutProvider in case no TimeoutProvider
 418      * is set (or it is set to null).
 419      * The default implementation is 10 minutes scaled by
 420      * a value found in the environment ("javatestTimeoutFactor").
 421      * @see #setTimeoutProvider(com.sun.javatest.Script.TimeoutProvider)
 422      * @see #getTestTimeout()
 423      * @see TimeoutProvider
 424      */
 425     public TimeoutProvider getTimeoutProvider() {
 426         if(provider == null) {
 427             provider = new DefaultTimeoutProvider();
 428         }
 429         return provider;
 430     }
 431 
 432     /**
 433      * Get the timeout to be used for a test.
 434      * Uses TimeoutProvider to get test timeout value. The default
 435      * implementation of TimeoutProvider is 10 minutes scaled by
 436      * a value found in the environment ("javatestTimeoutFactor").
 437      * This method can be overriden to provide different behaviors.
 438      * A value of zero means no timeout.
 439      * @return the number of seconds in which the test is expected to
 440      * complete its execution.
 441      * @see #getTimeoutProvider()
 442      * @see #setTimeoutProvider(com.sun.javatest.Script.TimeoutProvider)
 443      * @see TimeoutProvider
 444      */
 445     protected int getTestTimeout() {
 446         // use "getTimeoutProvider()." instead of "provider." to generate default (10min*factor) timeout provider
 447         return getTimeoutProvider().getTestTimeout();
 448     }
 449 
 450     /**
 451      * Compile the given source files individually. One at a time, each source file
 452      * is passed to <em>compileTogether</em>, until they have all been
 453      * successfully compiled, or until one fails to compile.
 454      * @param srcs      The names of the file to be compiled.
 455      * @return          The status of the compilation: passed or failed.
 456      * @see #compileTogether
 457      */
 458     protected Status compileIndividually(String[] srcs) {
 459         return compileIndividually(DEFAULT_COMPILE_COMMAND, srcs);
 460     }
 461 
 462     /**
 463      * Compile the given source files individually. One at a time, each source file
 464      * is passed to <em>compileTogether</em>, until they have all been
 465      * successfully compiled, or until one fails to compile.
 466      * @param command the base name of the command entry in the environment to be used
 467      * to compile any necessary sources. The complete entry name will be
 468      * <code>command.</code><i>command</i><code>.</code><i>extn</i>
 469      * @param srcs      The names of the file to be compiled.
 470      * @return          The status of the compilation: passed or failed.
 471      * @see #compileTogether
 472      */
 473     protected Status compileIndividually(String command, String[] srcs) {
 474         if (srcs.length == 0)
 475             return error_noSource;
 476 
 477         for (int i = 0; i < srcs.length; i++) {
 478             Status s = compileOne(command, srcs[i]);
 479             if (!s.isPassed())
 480                 return s;
 481         }
 482         return pass_compSuccExp;
 483     }
 484 
 485     /**
 486      * Compile the given source files individually. One at a time, each source file
 487      * is passed to <em>compileTogether</em>, until they have all been
 488      * successfully compiled, or until one fails to compile.
 489      * @param srcs      The names of the file to be compiled.
 490      * @return          The status of the compilation: passed or failed.
 491      * @see #compileTogether
 492      */
 493     protected Status compileIndividually(File[] srcs) {
 494         return compileIndividually(DEFAULT_COMPILE_COMMAND, filesToStrings(srcs));
 495     }
 496 
 497     /**
 498      * Compile the given source files individually. One at a time, each source file
 499      * is passed to <em>compileTogether</em>, until they have all been
 500      * successfully compiled, or until one fails to compile.
 501      * @param command the base name of the command entry in the environment to be used
 502      * to compile any necessary sources. The complete entry name will be
 503      * <code>command.</code><i>command</i><code>.</code><i>extn</i>
 504      * @param srcs      The names of the file to be compiled.
 505      * @return          The status of the compilation: passed or failed.
 506      * @see #compileTogether
 507      */
 508     protected Status compileIndividually(String command, File[] srcs) {
 509         return compileIndividually(command, filesToStrings(srcs));
 510     }
 511 
 512     /**
 513      * Compile the given source file.
 514      * @param src       The name of the file to be compiled.
 515      * @return          The status of the compilation: passed or failed.
 516      * @see #compileTogether
 517      */
 518     protected Status compileOne(String src) {
 519         return compileOne(DEFAULT_COMPILE_COMMAND, src);
 520     }
 521 
 522     /**
 523      * Compile the given source file. The file is treated as a singleton group
 524      * and passed to <em>compileTogether</em>.
 525      * @param command the base name of the command entry in the environment to be used
 526      * to compile any necessary sources. The complete entry name will be
 527      * <code>command.</code><i>command</i><code>.</code><i>extn</i>
 528      * @param src       The name of the file to be compiled.
 529      * @return          The status of the compilation: passed or failed.
 530      * @see #compileTogether
 531      */
 532     protected Status compileOne(String command, String src) {
 533         return compileTogether(command, new String[] {src});
 534     }
 535 
 536     /**
 537      * Compiles the given source file.
 538      *
 539      * @param src       The name of the file to be compiled.
 540      * @return          The status of the compilation: passed or failed.
 541      * @see #compileTogether
 542      */
 543     protected Status compileOne(File src) {
 544         return compileOne(DEFAULT_COMPILE_COMMAND, src.getPath());
 545     }
 546 
 547     /**
 548      * Compiles the given source file.
 549      *
 550      * @param command the base name of the command entry in the environment to be used
 551      * to compile any necessary sources. The complete entry name will be
 552      * <code>command.</code><i>command</i><code>.</code><i>extn</i>
 553      * @param src       The name of the file to be compiled.
 554      * @return          The status of the compilation: passed or failed.
 555      * @see #compileTogether
 556      */
 557     protected Status compileOne(String command, File src) {
 558         return compileOne(command, src.getPath());
 559     }
 560 
 561     /**
 562      * Compile the given source files together.  The compiler and arguments to be used
 563      * are identified by the `<code>env.<em>env</em>.compile.<em>extn</em>.*</code>'
 564      * properties in the script's environment, where <em>env</em>
 565      * is the name of the environment specified to the GUI, and <em>extn</em> is
 566      * the extension of the first source file.  The names of the files to be compiled
 567      * are added to the end of the arguments retrieved from the environment.
 568      * @param srcs      The names of the file to be compiled.
 569      * @return          The status of the compilation: passed or failed.
 570      * @see #invokeCommand
 571      */
 572     protected Status compileTogether(String[] srcs) {
 573         return compileTogether(DEFAULT_COMPILE_COMMAND, srcs);
 574     }
 575 
 576     /**
 577      * Compile the given source files together.  The compiler and arguments to be used
 578      * are identified by the `<code>env.<em>env</em>.command.<em>command</em>.<em>extn</em>.*</code>'
 579      * properties in the script's environment, where <em>env</em>
 580      * is the name of the environment specified to the GUI, and <em>extn</em> is
 581      * the extension of the first source file.  The names of the files to be compiled
 582      * are added to the end of the arguments retrieved from the environment.
 583      * @param command the base name of the command entry in the environment to be used
 584      * to compile any necessary sources. The complete entry name will be
 585      * <code>command.</code><i>command</i><code>.</code><i>extn</i>
 586      * @param srcs      The names of the file to be compiled.
 587      * @return          The status of the compilation: passed or failed.
 588      * @see #invokeCommand
 589      */
 590     protected Status compileTogether(String command, String[] srcs) {
 591         if (srcs.length == 0)
 592             return error_noSource;
 593 
 594         try {
 595             String[] classDir = env.lookup("testClassDir");
 596             if (classDir == null || classDir.length != 1)
 597                 return error_badTestClassDir;
 598             File f = new File(classDir[0]);
 599             if (!f.exists())
 600                 f.mkdirs();
 601         }
 602         catch (TestEnvironment.Fault e) {
 603             return error_badTestClassDir;
 604         }
 605 
 606         String primarySrcFile = srcs[0];
 607         int dot = primarySrcFile.lastIndexOf('.');
 608         if (dot == -1)
 609             return error_noExtnInSource;
 610 
 611         String extn = primarySrcFile.substring(dot);
 612 
 613         env.put("testSource", srcs);
 614 
 615         try {
 616             boolean ok = sourceTable.acquire(srcs, 10*60*1000);
 617             if (!ok)
 618                 return Status.error(i18n.getString("script.srcLockTimeout"));
 619             return invokeCommand(command + extn);
 620         }
 621         catch (InterruptedException e) {
 622             return Status.error(i18n.getString("script.srcLockInterrupted"));
 623         }
 624         finally {
 625             sourceTable.release(srcs);
 626         }
 627     }
 628 
 629     private static ResourceTable sourceTable = new ResourceTable();
 630 
 631     /**
 632      * Compile the given source files together.  The compiler and arguments to be used
 633      * are identified by the `<code>env.<em>env</em>.command.compile.<em>extn</em>.*</code>'
 634      * properties in the script's environment, where <em>env</em>
 635      * is the name of the environment specified to the GUI, and <em>extn</em> is
 636      * the extension of the first source file.  The names of the files to be compiled
 637      * are added to the end of the arguments retrieved from the environment.
 638      * @param srcs      The names of the file to be compiled.
 639      * @return          The status of the compilation: passed or failed.
 640      * @see #invokeCommand
 641      */
 642     protected Status compileTogether(File[] srcs) {
 643         return compileTogether(DEFAULT_COMPILE_COMMAND, filesToStrings(srcs));
 644     }
 645 
 646     /**
 647      * Compile the given source files together.  The compiler and arguments to be used
 648      * are identified by the `<code>env.<em>env</em>.command.<em>command</em>.<em>extn</em>.*</code>'
 649      * properties in the script's environment, where <em>env</em>
 650      * is the name of the environment specified to the GUI, and <em>extn</em> is
 651      * the extension of the first source file.  The names of the files to be compiled
 652      * are added to the end of the arguments retrieved from the environment.
 653      * @param command the base name of the command entry in the environment to be used
 654      * to compile any necessary sources. The complete entry name will be
 655      * <code>command.</code><i>command</i><code>.</code><i>extn</i>
 656      * @param srcs      The names of the file to be compiled.
 657      * @return          The status of the compilation: passed or failed.
 658      * @see #invokeCommand
 659      */
 660     protected Status compileTogether(String command, File[] srcs) {
 661         return compileTogether(command, filesToStrings(srcs));
 662     }
 663 
 664     /**
 665      * Compile those source files for which the corresponding class file appears to
 666      * be out of date. Each source file is scanned to find a package statement to help
 667      * determine the main class defined in the source file -- the corresponding class
 668      * file in the given class directory is then checked, and if the source file is newer,
 669      * it is put on a list to be recompiled. After checking all the source files, if any
 670      * need to be recompiled, they will be compiled together, using the default compile
 671      * command ("command.compile.extn") entry in the the environment.
 672      * @param srcs The names of the source files to be compiled if necessary
 673      * @param classDir The class directory in which the corresponding class files
 674      * (if any) will be found.
 675      * @return          The status of the compilation: passed or failed.
 676      * @see #compileTogether
 677      */
 678     protected Status compileIfNecessary(String[] srcs, String classDir) {
 679         return compileIfNecessary(DEFAULT_COMPILE_COMMAND, srcs, classDir);
 680     }
 681 
 682     /**
 683      * Compile those source files for which the corresponding class file appears to
 684      * be out of date. Each source file is scanned to find a package statement to help
 685      * determine the main class defined in the source file -- the corresponding class
 686      * file in the given class directory is then checked, and if the source file is newer,
 687      * it is put on a list to be recompiled. After checking all the source files, if any
 688      * need to be recompiled, they will be compiled together, using the specified compile
 689      * command entry in the the environment.
 690      * @param command the base name of the command entry in the environment to be used
 691      * to compile any necessary sources. The complete entry name will be
 692      * <code>command.</code><i>command</i><code>.</code><i>extn</i>
 693      * @param srcs The names of the source files to be compiled if necessary
 694      * @param classDir The class directory in which the corresponding class files
 695      * (if any) will be found.
 696      * @return          The status of the compilation: passed or failed.
 697      * @see #compileTogether
 698      */
 699     protected Status compileIfNecessary(String command, String[] srcs, String classDir) {
 700         if (srcs.length == 0)
 701             return error_noSource;
 702 
 703         if (classDir == null)
 704             classDir = "$testClassDir";
 705 
 706         if (classDir.startsWith("$")) {
 707             try {
 708                 String[] cd = env.resolve(classDir);
 709                 if (cd == null || cd.length != 1)
 710                     return error_badTestClassDir;
 711                 classDir = cd[0];
 712             }
 713             catch (TestEnvironment.Fault e) {
 714                 return error_badTestClassDir;
 715             }
 716         }
 717 
 718         File cdf = new File(classDir);
 719         if (!cdf.exists())
 720             cdf.mkdirs();
 721 
 722         Vector<String> v = new Vector<>(0, srcs.length);
 723 
 724         for (int i = 0; i < srcs.length; i++) {
 725             String src = srcs[i];
 726             int x = src.lastIndexOf(File.separatorChar);
 727             int y = src.indexOf('.', x+1);
 728             String className = src.substring(x+1, (y == -1 ? src.length() : y));
 729             String pkgPrefix;  // deliberately unset to have compiler check init in all required cases
 730 
 731             // read the source file to see if a package statement exists
 732             // if it does, set pkgPrefix to package directory
 733             // if none found, set setPrefix to empty string
 734             // if error, report the error, ignore pkgPrefix, and set file to
 735             // be unconditionally compiled.
 736             try (BufferedReader r = new BufferedReader(new FileReader(src))) {
 737                 StreamTokenizer tr = new StreamTokenizer(r);
 738                 tr.ordinaryChar('/');
 739                 tr.slashStarComments(true);
 740                 tr.slashSlashComments(true);
 741                 tr.wordChars('.', '.'); // package separator
 742                 int c = tr.nextToken();
 743                 if (c == StreamTokenizer.TT_WORD && tr.sval.equals("package")) {
 744                     // found what looks like a package statement
 745                     c = tr.nextToken();
 746                     if (c == StreamTokenizer.TT_WORD)
 747                         // yes, it was a valid package statement
 748                         pkgPrefix = tr.sval.replace('.', File.separatorChar) + File.separatorChar;
 749                     else {
 750                         // well, sort of; malformed package statement
 751                         trOut.println(i18n.getString("script.badPackage"));
 752                         v.addElement(src);
 753                         continue;
 754                     }
 755                 }
 756                 else
 757                     // no package statement
 758                     pkgPrefix = "";
 759             }
 760             catch (IOException e) {
 761                 trOut.println(i18n.getString("script.badDateStamp", new Object[] { src, e }));
 762                 v.addElement(src);
 763                 continue;
 764             }
 765 
 766 
 767             File srcFile = new File(src);
 768             File classFile = new File(classDir, pkgPrefix + className + ".class");
 769             //System.out.println("checking " + classFile);
 770             //System.out.println("classfile " + classFile.lastModified());
 771             //System.out.println("srcfile " + srcFile.lastModified());
 772             if (classFile.exists() && classFile.lastModified() > srcFile.lastModified())
 773                 trOut.println(i18n.getString("script.upToDate", src));
 774             else
 775                 v.addElement(src);
 776         }
 777 
 778         if (v.size() > 0) {
 779             String[] necessarySrcs = new String[v.size()];
 780             v.copyInto(necessarySrcs);
 781 
 782             return compileTogether(command, necessarySrcs);
 783         }
 784         else
 785             return Status.passed(i18n.getString("script.allUpToDate"));
 786     }
 787 
 788     /**
 789      * Compile those source files for which the corresponding class file appears to
 790      * be out of date. Each source file is scanned to find a package statement to help
 791      * determine the main class defined in the source file -- the corresponding class
 792      * file in the given class directory is then checked, and if the source file is newer,
 793      * it is put on a list to be recompiled. After checking all the source files, if any
 794      * need to be recompiled, they will be compiled together, using the default compile
 795      * command ("command.compile.extn") entry in the the environment.
 796      * @param srcs The names of the source files to be compiled if necessary
 797      * @param classDir The class directory in which the corresponding class files
 798      * (if any) will be found.
 799      * @return          The status of the compilation: passed or failed.
 800      * @see #compileTogether
 801      */
 802     protected Status compileIfNecessary(File[] srcs, String classDir) {
 803         return compileIfNecessary(DEFAULT_COMPILE_COMMAND, filesToStrings(srcs), classDir);
 804     }
 805 
 806     /**
 807      * Compile those source files for which the corresponding class file appears to
 808      * be out of date. Each source file is scanned to find a package statement to help
 809      * determine the main class defined in the source file -- the corresponding class
 810      * file in the given class directory is then checked, and if the source file is newer,
 811      * it is put on a list to be recompiled. After checking all the source files, if any
 812      * need to be recompiled, they will be compiled together, using the specified compile
 813      * command entry in the the environment.
 814      * @param command the base name of the command entry in the environment to be used
 815      * to compile any necessary sources. The complete entry name will be
 816      * <code>command.</code><i>command</i><code>.</code><i>extn</i>
 817      * @param srcs The names of the source files to be compiled if necessary
 818      * @param classDir The class directory in which the corresponding class files
 819      * (if any) will be found.
 820      * @return          The status of the compilation: passed or failed.
 821      * @see #compileTogether
 822      */
 823     protected Status compileIfNecessary(String command, File[] srcs, String classDir) {
 824         return compileIfNecessary(command, filesToStrings(srcs), classDir);
 825     }
 826 
 827     /**
 828      * Execute the given class with the given arguments, which need to be passed
 829      * to the environment for $ substitution and for splitting into separate strings.
 830      * @param executeClass      The name of the class to be executed
 831      * @param executeArgs       The arguments to be evaluated before passing to
 832      *                          the class to be executed
 833      * @return                  The status of the execution
 834      * @see #execute(java.lang.String, java.lang.String, java.lang.String)
 835      */
 836     protected Status execute(String executeClass, String executeArgs) {
 837         return execute(DEFAULT_EXECUTE_COMMAND, executeClass, executeArgs);
 838     }
 839 
 840     /**
 841      * Execute the given class with the given arguments, which need to be passed
 842      * to the environment for $ substitution and for splitting into separate strings.
 843      * @param command   The name of the command containing the template to be executed
 844      * @param executeClass      The name of the class to be executed
 845      * @param executeArgs       The arguments to be evaluated before passing to
 846      *                          the class to be executed
 847      * @return                  The status of the execution
 848      */
 849     protected Status execute(String command, String executeClass,
 850                              String executeArgs) {
 851         try {
 852             String[] args = (executeArgs == null ? nullArgs : env.resolve(executeArgs));
 853             if (excludedTestCases != null)
 854                 args = exclude(args, excludedTestCases);
 855             return execute(command, executeClass, args);
 856         }
 857         catch (TestEnvironment.Fault e) {
 858             trOut.println(i18n.getString("script.testEnvFault",
 859                                          new Object[] { executeArgs, e.toString() }));
 860             return error_badExecuteArgs;
 861         }
 862     }
 863 
 864     /**
 865      * Execute the given class with the given arguments.  The interpreter to be used
 866      * and its arguments are identified by the `<code>env.<em>env</em>.execute.*</code>'
 867      * properties in the script's environment, where <em>env</em>
 868      * is the name of the environment specified to the GUI. The class to be executed and
 869      * its arguments are added to the end of the arguments retrieved from the environment.
 870      * @param executeClass      The name of the class to be executed.
 871      * @param executeArgs       Any arguments to be passed to the class to be executed.
 872      * @return                  The status of the execution
 873      * @see #execute(java.lang.String, java.lang.String, java.lang.String[])
 874      */
 875     protected Status execute(String executeClass, String[] executeArgs) {
 876         return execute(DEFAULT_EXECUTE_COMMAND, executeClass, executeArgs);
 877     }
 878 
 879     /**
 880      * Execute the given class with the given arguments.  The interpreter to be used
 881      * and its arguments are identified by the `<code>env.<em>env</em>.<em>command</em>.*</code>'
 882      * properties in the script's environment, where <em>env</em>
 883      * is the name of the environment specified to the GUI. The class to be executed and
 884      * its arguments are added to the end of the arguments retrieved from the environment.
 885      * @param command   The name of the command containing the template to be executed
 886      * @param executeClass      The name of the class to be executed.
 887      * @param executeArgs       Any arguments to be passed to the class to be executed.
 888      * @return                  The status of the execution
 889      * @see #invokeCommand
 890      */
 891     protected Status execute(String command, String executeClass, String[] executeArgs) {
 892         if (executeClass == null || executeClass.length() == 0)
 893             return error_noExecuteClass;
 894         env.put("testExecuteClass", executeClass);
 895         env.put("testExecuteArgs", executeArgs);
 896         return invokeCommand(command);
 897     }
 898 
 899     /**
 900      * RMI Compile the given class files.  The compiler and arguments to be used
 901      * is identified by the `<code>env.<em>env</em>.command.rmic</code>'
 902      * property in the script's environment, where <em>env</em>
 903      * is the name of the environment specified to the GUI.
 904      * The name of the classes to be compiled by rmic is obtained from the
 905      * test description.
 906      * @param classes   The names of the classes to be compiled by rmic.
 907      * @return          The status of the compilation: passed or failed.
 908      * @see #invokeCommand
 909      */
 910     protected Status rmiCompile(String[] classes) {
 911         return rmiCompile(DEFAULT_RMIC_COMMAND, classes);
 912     }
 913 
 914     /**
 915      * RMI Compile the given class files.  The compiler and arguments to be used
 916      * is identified by the `<code>env.<em>env</em>.command.<em>command</em></code>'
 917      * property in the script's environment, where <em>env</em>
 918      * is the name of the environment specified to the GUI.
 919      * The name of the classes to be compiled by rmic is obtained from the
 920      * test description.
 921      * @param command   The name of the command containing the template to be compiled
 922      * @param classes   The names of the classes to be compiled by rmic.
 923      * @return          The status of the compilation: passed or failed.
 924      * @see #invokeCommand
 925      */
 926     protected Status rmiCompile(String command, String[] classes) {
 927         try {
 928             String[] classDir = env.lookup("testClassDir");
 929             if (classDir == null || classDir.length != 1)
 930                 return error_badTestClassDir;
 931             File f = new File(classDir[0]);
 932             if (!f.exists())
 933                 f.mkdirs();
 934         }
 935         catch (TestEnvironment.Fault e) {
 936             return error_badTestClassDir;
 937         }
 938 
 939         if (classes == null || classes.length == 0)
 940             return error_noRMIClasses;
 941 
 942         env.put("testRmicClasses", classes);
 943         // backwards compatibility
 944         env.put("testRmicClass", classes);
 945         return invokeCommand(command);
 946     }
 947 
 948 
 949     /**
 950      * Invoke a command in the environment identified by a given key.
 951      * The command is identified by looking up `<code>command.<em>key</em></code>'
 952      * property in the environment. The first word of this property identifies
 953      * the name of a class that should be an implementation of <code>Command</code>,
 954      * and the subsequent words are the arguments to be passed to a fresh instance
 955      * of that class, via its <code>run</code> method.
 956      * Standard library implementations of <code>Command</code> are available,
 957      * such as:
 958      * <DL>
 959      * <DT>com.sun.javatest.lib.ProcessCommand
 960      * <DD>Execute a command in a separate process
 961      * <DT>com.sun.javatest.lib.ExecStdTestSameJVMCmd
 962      * <DD>Execute a standard test in the same JVM as JT Harness
 963      * <DT>com.sun.javatest.agent.PassiveAgentCommand
 964      * <DD>Execute a command on a remote machine
 965      * </DL>
 966      * For full details, the documentation for the various appropriate classes.
 967      *
 968      * <p> The use of `<code>command.<em>key</em></code>' supercedes an earlier
 969      * mechanism involving multiple properties. For backwards compatibility,
 970      * if the `<code>command.<em>key</em></code>' property is not found, the
 971      * properties for the earlier mechanism are checked as well.
 972      *
 973      * @param key       The tag for the command to be executed
 974      * @return          A status giving the outcome of the command
 975      *
 976      * @see Command
 977      *
 978      */
 979     protected Status invokeCommand(String key) {
 980         TestResult.Section section;
 981         Status s = null;
 982 
 983         try {
 984             String[] command = env.lookup("command." + key);
 985 
 986             if (command.length == 0)
 987                 return Status.error(i18n.getString("script.noCommand",
 988                                                    new Object[] { env.getName(), key }));
 989 
 990             String className = command[0];
 991             String[] args = new String[command.length - 1];
 992             System.arraycopy(command, 1, args, 0, args.length);
 993 
 994             section = testResult.createSection(key);
 995 
 996             section.getMessageWriter().println(i18n.getString("script.command",
 997                                                               new Object[] {className, StringArray.join(args) }));
 998 
 999             try (PrintWriter out1 = section.createOutput(cmdOut1Name);
1000                  PrintWriter out2 = section.createOutput(cmdOut2Name)) {
1001 
1002                 s = invokeClass(className, args, out1, out2);
1003             }
1004 
1005             section.setStatus(s);
1006             return s;
1007         }
1008         catch (TestEnvironment.Fault e) {
1009             return Status.error(i18n.getString("script.badCommand",
1010                                                new Object[] { env.getName(), key }));
1011         }
1012     }
1013 
1014     /**
1015      * Set the default names of the two default output streams used when executing a
1016      * command.  In many cases these may correspond to the UNIX-style standard-out
1017      * and standard-error streams.  This API does not define what they are used for
1018      * though, and architects are encouraged to give descriptive names if possible.
1019      *
1020      * @param out1Name Name of the first stream.
1021      * @param out2Name Name of the second stream.
1022      */
1023     protected void setDefaultCommandStreamNames(String out1Name, String out2Name) {
1024         cmdOut1Name = out1Name;
1025         cmdOut2Name = out2Name;
1026     }
1027 
1028     /**
1029      * Create and run a Command object.
1030      * @param className The name of the class to load and instantiate.
1031      * @param args      The args to pass to the `run' method of the loaded object.
1032      * @return          The result identifies any problems that may occur in trying
1033      *                  to create and run the specified object, or if it succeeds,
1034      *                  it returns the result from calling the object's `run' method.
1035      * @see Command
1036      */
1037     private Status invokeClass(String className, String[] args,
1038                                PrintWriter out1, PrintWriter out2) {
1039         // this is the central place where we get to run what the user
1040         // says in the environment file:
1041         Command testCommand;
1042         try {
1043             Class c = (loader == null ? Class.forName(className) : loader.loadClass(className));
1044             testCommand = (Command)(c.newInstance());
1045         }
1046         catch (ClassCastException e) {
1047             return Status.error(i18n.getString("script.cantRunClass",
1048                                                new Object[] { className, Command.class.getName() }));
1049         }
1050         catch (ClassNotFoundException ex) {
1051             return Status.error(i18n.getString("script.cantFindClass",
1052                                                new Object[] { className, env.getName() }));
1053         }
1054         catch (IllegalAccessException ex) {
1055             return Status.error(i18n.getString("script.cantAccessClass",
1056                                                new Object[] { className, env.getName() }));
1057         }
1058         catch (IllegalArgumentException ex) {
1059             return Status.error(i18n.getString("script.badClassName",
1060                                                new Object[] { className, env.getName() }));
1061         }
1062         catch (InstantiationException ex) {
1063             return Status.error(i18n.getString("script.cantCreateClass",
1064                                                new Object[] { className, env.getName() }));
1065         }
1066         catch (ThreadDeath e) {
1067             throw (ThreadDeath)(e.fillInStackTrace());
1068         }
1069         catch (Exception e) {
1070             e.printStackTrace(out1);
1071             return Status.error(i18n.getString("script.unexpLoadExc", new Object[] { className, e }));
1072         }
1073         catch (Error e) {
1074             e.printStackTrace(out1);
1075             return Status.error(i18n.getString("script.unexpLoadErr", new Object[] { className, e }));
1076         }
1077         catch (Throwable e) {
1078             e.printStackTrace(out1);
1079             return Status.error(i18n.getString("script.unexpLoadThr", new Object[] { className, e }));
1080         }
1081 
1082         try {
1083             testCommand.setClassLoader(loader);
1084             return testCommand.run(args, out1, out2);
1085         }
1086         catch (ThreadDeath e) {
1087             throw (ThreadDeath)(e.fillInStackTrace());
1088         }
1089         catch (Exception e) {
1090             e.printStackTrace(out1);
1091             // error reduced to failed in following line for benefit of negative tests
1092             return Status.failed(i18n.getString("script.unexpExecExc", new Object[] { className, e }));
1093         }
1094         catch (Error e) {
1095             e.printStackTrace(out1);
1096             // error reduced to failed in following line for benefit of negative tests
1097             return Status.failed(i18n.getString("script.unexpExecErr", new Object[] { className, e }));
1098         }
1099         catch (Throwable e) {
1100             e.printStackTrace(out1);
1101             // error *NOT* reduced to failed in following line for benefit of
1102             // negative tests: test should never throw something which is not
1103             // an Exception or Error
1104             return Status.error(i18n.getString("script.unexpExecThr", new Object[] { className, e }));
1105         }
1106 
1107     }
1108 
1109     /**
1110      * Modify the args for a test to be executed, according to a set
1111      * of test cases to be excluded. If there are no test cases to be excluded,
1112      * the result will be the original args unchanged; otherwise, the
1113      * result will be the original args prefixed by "-exclude" and a
1114      * comma-separated list of exclude test cases.
1115      * @param args The basic list of args for the test
1116      * @param testCases the set of test cases to be excluded, or null if none
1117      * @return The original list of args, possibly prefixed by "-exclude"
1118      * and a comma-separated list of test cases that should not be executed by
1119      * the test
1120      */
1121     protected String[] exclude(String[] args, String[] testCases) {
1122         if (testCases == null)
1123             return args;
1124         StringBuffer sb = new StringBuffer();
1125         for (int i = 0; i < testCases.length; i++) {
1126             if (i > 0)
1127                 sb.append(",");
1128             sb.append(testCases[i]);
1129         }
1130         String[] newArgs = new String[args.length + 2];
1131         newArgs[0] = "-exclude";
1132         newArgs[1] = sb.toString();
1133         System.arraycopy(args, 0, newArgs, 2, args.length);
1134         testResult.putProperty("exclude", newArgs[1]);
1135         return newArgs;
1136     }
1137 
1138     /**
1139      * Utility routine to convert an array of filenames to a corresponding
1140      * array of strings.
1141      * @param files     The filenames to be converted
1142      * @return          The corresponding strings
1143      */
1144     protected static String[] filesToStrings(File[] files) {
1145         String[] strings = new String[files.length];
1146         for (int i = 0; i < files.length; i++)
1147             strings[i] = files[i].getPath();
1148         return strings;
1149     }
1150 
1151     /**
1152      * The test description for the test being performed.
1153      */
1154     protected TestDescription td;               // required
1155 
1156     /**
1157      * The set of test cases to be excluded for this test.
1158      */
1159     protected String[] excludedTestCases;       // optional, may be null
1160 
1161     /**
1162      * The test environment for the test being performed.
1163      */
1164     protected TestEnvironment env;              // required
1165 
1166     /**
1167      * The initialization args for the script.
1168      */
1169     protected String[] scriptArgs;              // optional
1170 
1171     /**
1172      * The work directory for the test run.
1173      */
1174     protected WorkDirectory workDir;                    // required
1175 
1176     /**
1177      * The default name for the TestResult section used to save the data written to the out1 stream
1178      * for a command.
1179      * @see Command#run
1180      */
1181     protected String cmdOut1Name = "out1";
1182 
1183     /**
1184      * The default name for the TestResult section used to save the data written to the out2 stream
1185      * for a command.
1186      * @see Command#run
1187      */
1188     protected String cmdOut2Name = "out2";
1189 
1190     /**
1191      * A backup policy object that specifies how files should be backed up,
1192      * if a file is found to exist when a new one of the same name is to be
1193      * written.
1194      */
1195     protected BackupPolicy backupPolicy = BackupPolicy.noBackups(); // optional
1196 
1197     /**
1198      * The class loader to be used to load additional user-specified classes
1199      * as required in the execution of the script.
1200      */
1201     protected ClassLoader loader;               // optional, may be null
1202 
1203     /**
1204      * The reporting channel for the test being performed.
1205      */
1206     protected PrintWriter trOut;
1207 
1208     // have to define this before the definitions that follow
1209     private static final I18NResourceBundle i18n = I18NResourceBundle.getBundleForClass(Script.class);
1210 
1211     // use getTimeoutProvider and setTimeoutProvider
1212     private TimeoutProvider provider = null;
1213 
1214     // convenience definitions
1215 
1216     /**
1217      * A status that may be used to indicate problems in the executeArgs field
1218      * of a test description.
1219      */
1220     protected static final Status
1221         error_badExecuteArgs = Status.error(i18n.getString("script.badExecuteArgs"));
1222 
1223     /**
1224      * A status that may be used to indicate a problem with a test's class directory.
1225      */
1226     protected static final Status
1227         error_badTestClassDir = Status.error(i18n.getString("script.badTestClassDir"));
1228 
1229     /**
1230      * A status that may be used to indicate that a compilation failed unexpectedly.
1231      */
1232     protected static final Status
1233         error_compFailUnexp = Status.error(i18n.getString("script.compFailUnexp"));
1234 
1235     /**
1236      * A status that may be used to indicate that no action was specified.
1237      */
1238     protected static final Status
1239         error_noActionSpecified = Status.error(i18n.getString("script.noAction"));
1240 
1241     /**
1242      * A status that may be used to indicate that no execute class was specified in a test description.
1243      */
1244     protected static final Status
1245         error_noExecuteClass = Status.error(i18n.getString("script.noExecuteClass"));
1246 
1247     /**
1248      * A status that may be used to indicate that no extension was found in a source file.
1249      */
1250     protected static final Status
1251         error_noExtnInSource = Status.error(i18n.getString("script.noExtnInSrc"));
1252 
1253     /**
1254      * A status that may be used to indicate that no rmi classes were specified in a test description.
1255      */
1256     protected static final Status
1257         error_noRMIClasses = Status.error(i18n.getString("script.noRMIClasses"));
1258 
1259     /**
1260      * A status that may be used to indicate that no sources were specified in a test description.
1261      */
1262     protected static final Status
1263         error_noSource = Status.error(i18n.getString("script.noSource"));
1264 
1265     /**
1266      * A status that may be used to indicate the a compilation failed unexpectedly.
1267      */
1268     protected static final Status
1269         fail_compFailUnexp = Status.failed(i18n.getString("script.compFailUnexp"));
1270 
1271     /**
1272      * A status that may be used to indicate that a compilation did not fail as was expected.
1273      */
1274     protected static final Status
1275         fail_compSuccUnexp = Status.failed(i18n.getString("script.compSuccUnexp"));
1276 
1277     /**
1278      * A status that may be used to indicate that a test execution step  did not fail as wqas expected.
1279      */
1280     protected static final Status
1281         fail_execSuccUnexp = Status.failed(i18n.getString("script.execSuccUnexp"));
1282 
1283     /**
1284      * A status that may be used to indicate that a compilation failed as expected.
1285      */
1286     protected static final Status
1287         pass_compFailExp = Status.passed(i18n.getString("script.compFailExp"));
1288 
1289     /**
1290      * A status that may be used to indicate that a compilation succeeded as expected.
1291      */
1292     protected static final Status
1293         pass_compSuccExp = Status.passed(i18n.getString("script.compSuccExp"));
1294 
1295     /**
1296      * A status that may be used to indicate that an execution step failed, as was expected.
1297      */
1298     protected static final Status
1299         pass_execFailExp = Status.passed(i18n.getString("script.execFailExp"));
1300 
1301     // backwards compatibility
1302     /**
1303      * A status that may be used to indicate that no source files were found in the test description.
1304      */
1305     protected static final Status noSource = error_noSource;
1306 
1307     /**
1308      * A status that may be used to indicate that no extension was found in a source file.
1309      */
1310     protected static final Status noExtnInSource = error_noExtnInSource;
1311 
1312     private static final String[] nullArgs = { };
1313     private static final String DEFAULT_COMPILE_COMMAND = "compile";
1314     private static final String DEFAULT_EXECUTE_COMMAND = "execute";
1315     private static final String DEFAULT_RMIC_COMMAND = "rmic";
1316     private static final String defaultClassDir = "classes";
1317     private static String osInfo;
1318 
1319     /**
1320      * A timer that may be used to set up timeouts.
1321      */
1322     protected static final Timer alarmTimer = new Timer();
1323 
1324     private TestResult testResult;
1325     private Alarm alarm;
1326     private boolean jtrIfPassed =
1327         System.getProperty("javatest.script.jtrIfPassed", "true").equals("true");
1328 
1329     /**
1330      * Notifier of starting/finishing tests.
1331      * Initialized only when useNotifer() returns true.
1332      * @see #useNotifier
1333      * @see #setNotifier
1334      * @since 4.2.1
1335      */
1336     protected Harness.Observer notifier;
1337 
1338     /**
1339      * Returns true if the Script uses own way of notifying the Harness
1340      * of starting/finishing test, false otherwise (by default).
1341      *
1342      * Normally the Harness notifies all listeners of an event of
1343      * starting a test when the method run() is invoked and an event of
1344      * finishing the test when the method run() is completed. Those Scripts
1345      * which need to take a control over notifying should override this method
1346      * to return <code>true</code>. In this case the <i>notifier</i> field will
1347      * be initialized and the Harness will no longer notify the listeners when
1348      * a test starts/stops.
1349      * @since 4.2.1
1350      */
1351     public boolean useNotifier() {
1352         return false;
1353     }
1354 
1355     /**
1356      * Sets notifier to be used to inform listeners of events of a test
1357      * starting/finishing. Invoked by the Harness iff useNotifier()
1358      * returns true.
1359      *
1360      * @see #useNotifier
1361      * @since 4.2.1
1362      */
1363     public void setNotifier(Harness.Observer notifier) {
1364         this.notifier = notifier;
1365     }
1366 
1367     /**
1368      * Interface for extended testTimeout control. Use setTimeoutProvider to
1369      * change test timeout value
1370      * @see #setTimeoutProvider(TimeoutProvider)
1371      */
1372     public static interface TimeoutProvider {
1373         /**
1374          * Implement this method returning desired test timeout value
1375          *
1376          * @return timeout in <b>seconds</b>
1377          */
1378         public int getTestTimeout();
1379     }
1380 
1381     private class DefaultTimeoutProvider implements TimeoutProvider {
1382         public int getTestTimeout() {
1383             float factor = 1;
1384             try {
1385                 String[] jtf = env.lookup("javatestTimeoutFactor");
1386                 if (jtf != null) {
1387                     if (jtf.length == 1)
1388                         factor = Float.parseFloat(jtf[0]);
1389                     else if (jtf.length == 2)
1390                         factor = Float.parseFloat(jtf[1]);
1391                 }
1392             }
1393             catch (TestEnvironment.Fault e) {
1394             }
1395             return (int) (600 * factor); // 60 * 10 = 600 sec = 10 min
1396         }
1397     }
1398 
1399     private class Alarm implements Timer.Timeable {
1400         Alarm(int delay) {
1401             this(delay, Thread.currentThread());
1402         }
1403 
1404         Alarm(int delay, Thread threadToInterrupt) {
1405             if (threadToInterrupt == null)
1406                 throw new NullPointerException();
1407 
1408             this.delay = delay;
1409             this.threadToInterrupt = threadToInterrupt;
1410             entry = alarmTimer.requestDelayedCallback(this, delay);
1411             if (debugAlarm)
1412                 System.err.println(i18n.getString("script.alarm.started", this));
1413         }
1414 
1415         synchronized void cancel() {
1416             if (debugAlarm)
1417                 System.err.println(i18n.getString("script.alarm.cancelled", this));
1418             alarmTimer.cancel(entry);
1419         }
1420 
1421         public synchronized void timeout() {
1422             if (count == 0)
1423                 trOut.println(i18n.getString("script.timeout", new Float(delay/1000.f)));
1424             else if (count%100 == 0) {
1425                 trOut.println(i18n.getString("script.notResponding", new Integer(count)));
1426                 if (count%1000 == 0)
1427                     System.err.println(i18n.getString("script.timedOut",
1428                                                       new Object[] { td.getRootRelativeURL(), new Integer(count) }));
1429             }
1430             if (debugAlarm)
1431                 System.err.println(i18n.getString("script.alarm.interrupt", new Object[] { this, threadToInterrupt }));
1432             threadToInterrupt.interrupt();
1433             count++;
1434             entry = alarmTimer.requestDelayedCallback(this, 100); // keep requesting interrupts until cancelled
1435         }
1436 
1437         private int delay;
1438         private Thread threadToInterrupt;
1439         private int count;
1440         private Timer.Entry entry;
1441     }
1442 
1443     private static boolean debugAlarm = Boolean.getBoolean("debug.com.sun.javatest.Script.Alarm");
1444 }