blob: e3a1089b54c62ae5628f2092da74490f7f398261 [file] [log] [blame]
/*
* Copyright (c) 2010, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package vm.mlvm.share;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import nsk.share.ArgumentParser;
import nsk.share.Log;
import nsk.share.Log.TraceLevel;
import nsk.share.test.StressOptions;
import nsk.share.test.Stresser;
import vm.share.options.Option;
import vm.mlvm.share.ExceptionsOptionObjectFactory;
/**
* The base class for MLVM tests.
* Subclasses need to override {@link #run()} method to implement test logic.
*/
public abstract class MlvmTest {
/**
* MLVM tests are expected to implement this method to provide the logic.
*
* @return true if test passed, false if failed
* @throws Throwable any subclass of Throwable to indicate test failure
*/
public abstract boolean run() throws Throwable;
/** Performs pre-run (prolog) actions in MlvmTest subclasses.
* The default implementation does nothing.
* Sublcasses may override this method to perform custom actions after test is initialized
* (initialization order is described in MlvmTestExecutor class) but before {@link run()} method is invoked.
* @throws Throwable in case of problem, which is interpreted as a test failure
* @see MlvmTestExecutor
*/
protected void initializeTest() throws Throwable {
}
/** Performs post-run (epilog) actions.
* This method is executed after the {@link #run()} method.
* Does nothing by default.
* Subclasses may override this method when some finalization actions are required.
* Test fails if this method throws exception.
* @param result test execution status: true, if test passed, false otherwise
* @throws Throwable may throw any subclass of Throwable to indicate test failure (regardless of run() method result)
* @see MlvmTestExecutor
*/
protected void finalizeTest(boolean result) throws Throwable {
}
/**
* Resets the tests between runs.
* You may override this method, especially if your test supports -stressRunsFactor option
* @throws Throwable may throw any subclass of Throwable to indicate test failure (regardless of run() method result)
* @see MlvmTestExecutor
*/
protected void resetTest() throws Throwable {
testMarkedFailed = false;
}
// Options for all MlvmTests
@Option(name = "requireExceptions", default_value = "", factory = ExceptionsOptionObjectFactory.class,
description = "Specifying this option turns test into negative one: "
+ "the specified exception class names separated with commas have to be caught for the test to pass")
private List<Class<? extends Throwable>> requiredExceptionClasses = new ArrayList<>();
@Option(name = "seed", default_value = "0", description = "Initial random seed")
private long _seed;
@Option(name = "runs", default_value = "1", description = "How many times the test should be re-run")
private int runs = 1;
// Some internal stuff
private static MlvmTest instance;
/**
* Sets internal static variable to instance of the test.
* Used in debugger/debuggee tests.
* Not intended to work if there are several MlvmTests created.
* @param inst Instance of the test
*/
public static void setInstance(MlvmTest inst) {
instance = inst;
}
/**
* Returns internal static variable holding instance of the test, which was set using {@link #setInstance()}.
* Used in debugger/debuggee tests.
* Not intended to work if there are several MlvmTests created.
* @return Instance of the test
*/
public static MlvmTest getInstance() {
return instance;
}
private static String name = "Test";
/**
* Sets internal static variable to the name of the test.
* Debugger/debuggee MLVM tests use this feature to differentiate logging from debugger and debuggee
* Not intended to work if there are several MlvmTests created
* @param n Name of the test
*/
public static void setName(String n) {
name = n;
}
/**
* Returns internal static variable holding the name of the test.
* Debugger/debuggee MLVM tests use this feature to differentiate logging from debugger and debuggee
* Not intended to work if there are several MlvmTests created
* @return Name of the test
*/
public static String getName() {
return name;
}
void initRNG() {
if (_seed != 0) {
Env.setRNGSeed(_seed);
}
Env.traceVerbose("Initial seed = " + _seed);
}
/**
* Sets number of test runs
* @param r Number of test runs
*/
public void setRunsNumber(int r) {
runs = r;
}
/**
* Return number of test runs
* @return Number of test runs
*/
public int getRunsNumber() {
return runs;
}
// Sugar...
/**
* Provides Random Number Generator for the test. The tests should always use this generator
* to guarantee repeatability (using -seed option), especially in multi-threaded usages
* @return Random number generator for this thread, seeded with command-line option, if provided
*/
public static Random getRNG() {
return Env.getRNG();
}
/**
* Returns logger, which is used in all MLVM framework. This guarantees correct ordering of messages
* @return Logger object
*/
public static Log getLog() {
return Env.getLog();
}
/**
* ArgumentParser is the old implementation of command-line parser (the new tests should use
* vm.share.options framework). However it is maintained, because nsk JDI/SAJDI framework is built
* on ArgumentParser.
* @return ArgumentParser object created with command-line options (see {@link MlvmTestExecutor}
* for details)
*/
public static ArgumentParser getArgumentParser() {
return Env.getArgParser();
}
// ...and spice
/* Makes the test "negative": one of the specified exception classes has to be thrown by the test to pass.
* Test fails if exception has not been thrown.
* Boolean value returned by {@link run()} method is ignored.
* Calling {@link #markTestFailed()} causes test to fail anyway.
* <p>
* Invoke this method BEFORE run() method (e.g., in prolog) to instruct launcher
* to anticipate the exception instead of the positive (normal) mode.
* @param classes The list of exception classes
* Empty list or null indicates that test is positive.
*/
@SafeVarargs
public final void setRequiredExceptions(Class<? extends Throwable>... classes) {
setRequiredExceptions(Arrays.asList(classes));
}
/* Makes the test "negative": one of the specified exception classes has to be thrown by the test to pass.
* Test fails if exception has not been thrown.
* Boolean value returned by {@link run()} method is ignored.
* Calling {@link #markTestFailed()} causes test to fail anyway.
* <p>
* Invoke this method BEFORE run() method (e.g., in prolog) to instruct launcher
* @param classes The list of exception classes.
* Empty list or null indicates that test is positive (in its standard form)
*/
public final void setRequiredExceptions(List<Class<? extends Throwable>> classes) {
if (requiredExceptionClasses.size() > 0) {
Env.traceNormal("Expected exceptions specified in the test are overridden in command-line");
return;
}
requiredExceptionClasses = classes;
}
/**
* Returns the list of required exceptions
* (please see {@link #setRequiredExceptions(Class<? extends Throwable>... classes)} method for details.
* @return The list of exception classes. Empty list or null indicates that test is positive (in its standard form)
*/
public final List<Class<? extends Throwable>> getRequiredExceptions() {
return requiredExceptionClasses;
}
private boolean testMarkedFailed = false;
/**
* Marks the test as failed.
* Regardless of run() method return value, the test is considered failed. Operation is not reversible.
* Can be called from multiple threads
*/
protected final void markTestFailed() {
markTestFailed(null, null);
}
/**
* Marks the test as failed, indicating falure reason.
* Regardless of run() method return value, the test is considered failed. Operation is not reversible.
* Can be called from multiple threads
* @param msg A message to log (using Log.complain() method)
*/
protected final void markTestFailed(String msg) {
markTestFailedImpl(msg, null);
}
/**
* Marks the test as failed, indicating falure reason and exception, which caused it.
* Regardless of run() method return value, the test is considered failed. Operation is not reversible.
* Can be called from multiple threads
* @param msg A message to log (using Log.complain() method)
* @param t An exception to log
*/
protected final void markTestFailed(String msg, Throwable t) {
markTestFailedImpl(msg, t);
}
private synchronized void markTestFailedImpl(String msg, Throwable t) {
testMarkedFailed = true;
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
Env.complain(t, "%s marked failed at %s%s", getName(), stackTrace[3],
msg == null ? "" : ":\n" + msg);
}
/**
* Checks if the test has marked failed.
* @return true, if the test marked failed
*/
protected final synchronized boolean isMarkedFailed() {
return testMarkedFailed;
}
private static boolean dumpHeapAfter = false;
/**
* Checks if heap dump requestd after running the test.
* @return true, if the test marked failed
* @see MlvmTestExecutor for heap dumping details.
*/
public static synchronized boolean getHeapDumpAfter() {
return dumpHeapAfter;
}
/**
* Sets or clears heap dumping request. Heap is dumped in MlvmTestExecutor after running the test.
*
* NB. heap dumping uses ProcessUtils libraries, so it should be added to library path in cfg-file:
* {@code export LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${COMMON_LIBS_LOCATION}/lib/${ARCH}/vm/share"}
* @param enable true, if heap should be dumped, false if not
* @see MlvmTestExecutor for heap dumping details.
*/
public static synchronized void setHeapDumpAfter(boolean enable) {
dumpHeapAfter = enable;
}
protected static Stresser createStresser() {
Stresser s = new Stresser(getArgumentParser().getStressOptions());
if (getLog().getTraceLevel() >= TraceLevel.TRACE_VERBOSE) {
s.printStressInfo(getLog().getOutStream());
}
return s;
}
protected static StressOptions getStressOptions() {
return getArgumentParser().getStressOptions();
}
// Launchers are left here for compatibility. Launching code has been moved to MlvmTestExecutor
// TODO: A minor bug has to be filed to replace MlvmTest.launch() calls with MlvmTestExecutor.launch()
protected static void launch(ArgumentParser argumentParser) {
MlvmTestExecutor.launch(argumentParser);
}
protected static void launch(ArgumentParser argumentParser, Object[] constructorArgs) {
MlvmTestExecutor.launch(argumentParser, constructorArgs);
}
protected static void launch(String[] args) {
MlvmTestExecutor.launch(args, null);
}
protected static void launch(String[] args, Object[] constructorArgs) {
MlvmTestExecutor.launch(args, constructorArgs);
}
}