1 /*
   2  * Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package vm.mlvm.share;
  25 
  26 import java.util.Random;
  27 import java.util.concurrent.atomic.AtomicBoolean;
  28 import java.util.List;
  29 import java.util.ArrayList;
  30 import java.util.Arrays;
  31 
  32 import nsk.share.ArgumentParser;
  33 import nsk.share.Log;
  34 import nsk.share.Log.TraceLevel;
  35 import nsk.share.test.StressOptions;
  36 import nsk.share.test.Stresser;
  37 import vm.share.options.Option;
  38 import vm.mlvm.share.ExceptionsOptionObjectFactory;
  39 
  40 /**
  41  * The base class for MLVM tests.
  42  * Subclasses need to override {@link #run()} method to implement test logic.
  43  */
  44 public abstract class MlvmTest {
  45 
  46     /**
  47      * MLVM tests are expected to implement this method to provide the logic.
  48      *
  49      * @return true if test passed, false if failed
  50      * @throws Throwable any subclass of Throwable to indicate test failure
  51      */
  52     public abstract boolean run() throws Throwable;
  53 
  54     /** Performs pre-run (prolog) actions in MlvmTest subclasses.
  55      * The default implementation does nothing.
  56      * Sublcasses may override this method to perform custom actions after test is initialized
  57      * (initialization order is described in MlvmTestExecutor class) but before {@link run()} method is invoked.
  58      * @throws Throwable in case of problem, which is interpreted as a test failure
  59      * @see MlvmTestExecutor
  60      */
  61     protected void initializeTest() throws Throwable {
  62     }
  63 
  64     /** Performs post-run (epilog) actions.
  65      * This method is executed after the {@link #run()} method.
  66      * Does nothing by default.
  67      * Subclasses may override this method when some finalization actions are required.
  68      * Test fails if this method throws exception.
  69      * @param result test execution status: true, if test passed, false otherwise
  70      * @throws Throwable may throw any subclass of Throwable to indicate test failure (regardless of run() method result)
  71      * @see MlvmTestExecutor
  72      */
  73     protected void finalizeTest(boolean result) throws Throwable {
  74     }
  75 
  76     /**
  77      * Resets the tests between runs.
  78      * You may override this method, especially if your test supports -stressRunsFactor option
  79      * @throws Throwable may throw any subclass of Throwable to indicate test failure (regardless of run() method result)
  80      * @see MlvmTestExecutor
  81      */
  82     protected void resetTest() throws Throwable {
  83         testMarkedFailed = false;
  84     }
  85 
  86     // Options for all MlvmTests
  87     @Option(name = "requireExceptions", default_value = "", factory = ExceptionsOptionObjectFactory.class,
  88             description = "Specifying this option turns test into negative one: "
  89                         + "the specified exception class names separated with commas have to be caught for the test to pass")
  90     private List<Class<? extends Throwable>> requiredExceptionClasses = new ArrayList<>();
  91 
  92     @Option(name = "seed", default_value = "0", description = "Initial random seed")
  93     private long _seed;
  94 
  95     @Option(name = "runs", default_value = "1", description = "How many times the test should be re-run")
  96     private int runs = 1;
  97 
  98     // Some internal stuff
  99     private static MlvmTest instance;
 100 
 101     /**
 102      * Sets internal static variable to instance of the test.
 103      * Used in debugger/debuggee tests.
 104      * Not intended to work if there are several MlvmTests created.
 105      * @param inst Instance of the test
 106      */
 107     public static void setInstance(MlvmTest inst) {
 108         instance = inst;
 109     }
 110 
 111     /**
 112      * Returns internal static variable holding instance of the test, which was set using {@link #setInstance()}.
 113      * Used in debugger/debuggee tests.
 114      * Not intended to work if there are several MlvmTests created.
 115      * @return Instance of the test
 116      */
 117     public static MlvmTest getInstance() {
 118         return instance;
 119     }
 120 
 121     private static String name = "Test";
 122 
 123     /**
 124      * Sets internal static variable to the name of the test.
 125      * Debugger/debuggee MLVM tests use this feature to differentiate logging from debugger and debuggee
 126      * Not intended to work if there are several MlvmTests created
 127      * @param n Name of the test
 128      */
 129     public static void setName(String n) {
 130         name = n;
 131     }
 132 
 133     /**
 134      * Returns internal static variable holding the name of the test.
 135      * Debugger/debuggee MLVM tests use this feature to differentiate logging from debugger and debuggee
 136      * Not intended to work if there are several MlvmTests created
 137      * @return Name of the test
 138      */
 139     public static String getName() {
 140         return name;
 141     }
 142 
 143     void initRNG() {
 144         if (_seed != 0) {
 145             Env.setRNGSeed(_seed);
 146         }
 147         Env.traceVerbose("Initial seed = " + _seed);
 148     }
 149 
 150     /**
 151      * Sets number of test runs
 152      * @param r Number of test runs
 153      */
 154     public void setRunsNumber(int r) {
 155         runs = r;
 156     }
 157 
 158     /**
 159      * Return number of test runs
 160      * @return Number of test runs
 161      */
 162     public int getRunsNumber() {
 163         return runs;
 164     }
 165 
 166     // Sugar...
 167     /**
 168      * Provides Random Number Generator for the test. The tests should always use this generator
 169      * to guarantee repeatability (using -seed option), especially in multi-threaded usages
 170      * @return Random number generator for this thread, seeded with command-line option, if provided
 171      */
 172     public static Random getRNG() {
 173         return Env.getRNG();
 174     }
 175 
 176     /**
 177      * Returns logger, which is used in all MLVM framework. This guarantees correct ordering of messages
 178      * @return Logger object
 179      */
 180     public static Log getLog() {
 181         return Env.getLog();
 182     }
 183 
 184     /**
 185      * ArgumentParser is the old implementation of command-line parser (the new tests should use
 186      * vm.share.options framework). However it is maintained, because nsk JDI/SAJDI framework is built
 187      * on ArgumentParser.
 188      * @return ArgumentParser object created with command-line options (see {@link MlvmTestExecutor}
 189      *         for details)
 190      */
 191     public static ArgumentParser getArgumentParser() {
 192         return Env.getArgParser();
 193     }
 194 
 195     // ...and spice
 196 
 197     /* Makes the test "negative": one of the specified exception classes has to be thrown by the test to pass.
 198      * Test fails if exception has not been thrown.
 199      * Boolean value returned by {@link run()} method is ignored.
 200      * Calling {@link #markTestFailed()} causes test to fail anyway.
 201      * <p>
 202      * Invoke this method BEFORE run() method (e.g., in prolog) to instruct launcher
 203      * to anticipate the exception instead of the positive (normal) mode.
 204      * @param classes The list of exception classes
 205      *                Empty list or null indicates that test is positive.
 206      */
 207     @SafeVarargs
 208     public final void setRequiredExceptions(Class<? extends Throwable>... classes) {
 209         setRequiredExceptions(Arrays.asList(classes));
 210     }
 211 
 212     /* Makes the test "negative": one of the specified exception classes has to be thrown by the test to pass.
 213      * Test fails if exception has not been thrown.
 214      * Boolean value returned by {@link run()} method is ignored.
 215      * Calling {@link #markTestFailed()} causes test to fail anyway.
 216      * <p>
 217      * Invoke this method BEFORE run() method (e.g., in prolog) to instruct launcher
 218      * @param classes The list of exception classes.
 219      *                Empty list or null indicates that test is positive (in its standard form)
 220      */
 221     public final void setRequiredExceptions(List<Class<? extends Throwable>> classes) {
 222         if (requiredExceptionClasses.size() > 0) {
 223             Env.traceNormal("Expected exceptions specified in the test are overridden in command-line");
 224             return;
 225         }
 226 
 227         requiredExceptionClasses = classes;
 228     }
 229 
 230     /**
 231      * Returns the list of required exceptions
 232      * (please see {@link #setRequiredExceptions(Class<? extends Throwable>... classes)} method for details.
 233      * @return The list of exception classes. Empty list or null indicates that test is positive (in its standard form)
 234      */
 235     public final List<Class<? extends Throwable>> getRequiredExceptions() {
 236         return requiredExceptionClasses;
 237     }
 238 
 239     private boolean testMarkedFailed = false;
 240 
 241     /**
 242      * Marks the test as failed.
 243      * Regardless of run() method return value, the test is considered failed. Operation is not reversible.
 244      * Can be called from multiple threads
 245      */
 246     protected final void markTestFailed() {
 247         markTestFailed(null, null);
 248     }
 249 
 250     /**
 251      * Marks the test as failed, indicating falure reason.
 252      * Regardless of run() method return value, the test is considered failed. Operation is not reversible.
 253      * Can be called from multiple threads
 254      * @param msg A message to log (using Log.complain() method)
 255      */
 256     protected final void markTestFailed(String msg) {
 257         markTestFailedImpl(msg, null);
 258     }
 259 
 260     /**
 261      * Marks the test as failed, indicating falure reason and exception, which caused it.
 262      * Regardless of run() method return value, the test is considered failed. Operation is not reversible.
 263      * Can be called from multiple threads
 264      * @param msg A message to log (using Log.complain() method)
 265      * @param t An exception to log
 266      */
 267     protected final void markTestFailed(String msg, Throwable t) {
 268         markTestFailedImpl(msg, t);
 269     }
 270 
 271     private synchronized void markTestFailedImpl(String msg, Throwable t) {
 272         testMarkedFailed = true;
 273 
 274         StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
 275         Env.complain(t, "%s marked failed at %s%s", getName(), stackTrace[3],
 276                      msg == null ? "" : ":\n" + msg);
 277 
 278     }
 279 
 280     /**
 281      * Checks if the test has marked failed.
 282      * @return true, if the test marked failed
 283      */
 284     protected final synchronized boolean isMarkedFailed() {
 285         return testMarkedFailed;
 286     }
 287 
 288     private static boolean dumpHeapAfter = false;
 289 
 290     /**
 291      * Checks if heap dump requestd after running the test.
 292      * @return true, if the test marked failed
 293      * @see MlvmTestExecutor for heap dumping details.
 294      */
 295     public static synchronized boolean getHeapDumpAfter() {
 296         return dumpHeapAfter;
 297     }
 298 
 299     /**
 300      * Sets or clears heap dumping request. Heap is dumped in MlvmTestExecutor after running the test.
 301      *
 302      * NB. heap dumping uses ProcessUtils libraries, so it should be added to library path in cfg-file:
 303      * {@code export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${COMMON_LIBS_LOCATION}/lib/${ARCH}/vm/share"}
 304      * @param enable true, if heap should be dumped, false if not
 305      * @see MlvmTestExecutor for heap dumping details.
 306      */
 307     public static synchronized void setHeapDumpAfter(boolean enable) {
 308         dumpHeapAfter = enable;
 309     }
 310 
 311     protected static Stresser createStresser() {
 312         Stresser s = new Stresser(getArgumentParser().getStressOptions());
 313         if (getLog().getTraceLevel() >= TraceLevel.TRACE_VERBOSE) {
 314             s.printStressInfo(getLog().getOutStream());
 315         }
 316         return s;
 317     }
 318 
 319     protected static StressOptions getStressOptions() {
 320         return getArgumentParser().getStressOptions();
 321     }
 322 
 323     // Launchers are left here for compatibility. Launching code has been moved to MlvmTestExecutor
 324     // TODO: A minor bug has to be filed to replace MlvmTest.launch() calls with MlvmTestExecutor.launch()
 325 
 326     protected static void launch(ArgumentParser argumentParser) {
 327         MlvmTestExecutor.launch(argumentParser);
 328     }
 329 
 330     protected static void launch(ArgumentParser argumentParser, Object[] constructorArgs) {
 331         MlvmTestExecutor.launch(argumentParser, constructorArgs);
 332     }
 333 
 334     protected static void launch(String[] args) {
 335         MlvmTestExecutor.launch(args, null);
 336     }
 337 
 338     protected static void launch(String[] args, Object[] constructorArgs) {
 339         MlvmTestExecutor.launch(args, constructorArgs);
 340     }
 341 
 342 }