Path: blob/master/test/hotspot/jtreg/vmTestbase/vm/mlvm/share/MlvmTestExecutor.java
41155 views
/*1* Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package vm.mlvm.share;2425import java.lang.reflect.Constructor;26import java.util.List;2728import nsk.share.Consts;29import nsk.share.ArgumentParser;30import vm.share.ProcessUtils;31import vm.share.options.IgnoreUnknownArgumentsHandler;32import vm.share.options.OptionSupport;3334/**35* This class executes a test based on (a subclass of) either:36* <ul>37* <li>{@link vm.mlvm.share.MlvmTest}38* <li>{@link java.lang.Runnable}39* </ul>40* using a number of launch() methods.41*42* Command-line parameters are parsed and set to the instance fields of the test marked with {@literal@}Option/Options annotations. See {@link vm.share.options} framework for details.43*44* Arguments for test constructor can be passed as well.45*46* Additionally the launch() methods:47* <ul>48* <li>measures test run time49* <li> handles all test status passing methods (via boolean return value, via MlvmTest.markTestFailed() calls, exception thrown from overriden run() method)50* <li>optionally dumps heap after test execution if MlvmTest.setHeapDumpAfer(true) was called51* </ul>52*53* @see vm.mlvm.share.MlvmTest54* @see vm.share.options55*56*/57public class MlvmTestExecutor {5859/**60* The heap dump file name. If you call MlvmTest.setHeapDumpAfter(true), the heap is dumped into file61* specified by this constant when test finishes62*/63public static final String HEAP_DUMP_FILENAME = "heap.dump";6465/**66* Launches MLVM test.67* Please see documentation for {@link #launch(Class<?> testClass, Object[] constructorArgs)} method.68*69* This version of the method is a syntactic shugar to launch test in this way:70*71* <code>72* public class MyTest extends MlvmTest {73* ...74* public static void main(String[] args) {75* MlvmTestExecutor.launch(new YourCustomArgumentParser(args));76* }77* }78* </code>79*80* @param args Command-line arguments, which are taken from supplied ArgumentParser (ArgumentParser is needed for compatibility with old tests)81and set to appropriate test instance fields using vm.share.options framework82* @see #launch(Class<?> testClass, Object[] constructorArgs)83*/84public static void launch(ArgumentParser argumentParser) {85launch(argumentParser, null);86}8788/**89* Launches MLVM test.90* Please see documentation for {@link #launch(Class<?> testClass, Object[] constructorArgs)} method.91*92* This version of the method is a syntactic shugar to launch test in this way:93*94* <code>95* public class MyTest extends MlvmTest {96* ...97* public static void main(String[] args) {98* MlvmTestExecutor.launch(new YourCustomArgumentParser(args), new Object[] { constructor-arguments... });99* }100* }101* </code>102*103* @param args Command-line arguments, which are taken from supplied ArgumentParser (ArgumentParser is needed for compatibility with old tests)104and set to appropriate test instance fields using vm.share.options framework105* @param constructorArgs Arguments, which are parsed to test constructor106* @see #launch(Class<?> testClass, Object[] constructorArgs)107*/108public static void launch(ArgumentParser argumentParser, Object[] constructorArgs) {109Env.init(argumentParser);110launch(constructorArgs);111}112113/**114* Launches MLVM test.115* Please see documentation for {@link #launch(Class<?> testClass, Object[] constructorArgs)} method.116*117* This version of the method is a syntactic shugar to launch test in this way:118*119* <code>120* public class MyTest extends MlvmTest {121* ...122* public static void main(String[] args) {123* MlvmTestExecutor.launch(args);124* }125* }126* </code>127*128* @param args Command-line arguments, which are parsed using internal ArgumentParser (for compatibility with old tests) and also set to appropriate test instance fields using vm.share.options framework129* @see #launch(Class<?> testClass, Object[] constructorArgs)130*/131public static void launch(String[] args) {132launch(args, null);133}134135/**136* Launches MLVM test.137* Please see documentation for {@link #launch(Class<?> testClass, Object[] constructorArgs)} method.138*139* This version of the method is a syntactic shugar to launch test in this way:140*141* <code>142* public class MyTest extends MlvmTest {143* ...144* public static void main(String[] args) {145* MlvmTestExecutor.launch(args, new Object[] { constructor-arguments... });146* }147* }148* </code>149*150* @param args Command-line arguments, which are parsed using internal ArgumentParser (for compatibility with old tests) and also set to appropriate test instance fields using vm.share.options framework151* @param constructorArgs Arguments, which are parsed to test constructor152* @see #launch(Class<?> testClass, Object[] constructorArgs)153*/154public static void launch(String[] args, Object[] constructorArgs) {155Env.init(args);156launch(constructorArgs);157}158159/**160* Launches MLVM test.161* Please see documentation for {@link #launch(Class<?> testClass, Object[] constructorArgs)} method.162*163* This version of the method is a syntactic shugar to launch test in this way:164*165* <code>166* public class MyTest extends MlvmTest {167* ...168* void aMethod() {169* ...170* MlvmTestExecutor.launch(new Object[] { constructor-arguments... });171* }172* }173* </code>174*175* @param constructorArgs Arguments, which are parsed to test constructor176* @see #launch(Class<?> testClass, Object[] constructorArgs)177*/178public static void launch(Object[] constructorArgs) {179Class<?> testClass = getTestClassFromCallerStack();180181if (testClass == null) {182throw new RuntimeException("TEST BUG: Can't find an instance of MlvmTest or Runnable in the stacktrace");183}184185launch(testClass, constructorArgs);186}187188private static Class<?> getTestClassFromCallerStack() {189try {190StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();191192// Elements of the stack trace: 0=Thread.getStackTrace(), 1rd=this method, 2nd=launch() method193// So we start searching from the most outer frame and finish searching at element 3194for (int i = stackTrace.length - 1; i >= 3; --i) {195StackTraceElement e = stackTrace[i];196Class<?> klass = Class.forName(e.getClassName());197if (MlvmTest.class.isAssignableFrom(klass)) {198return klass;199}200}201202return null;203} catch (ClassNotFoundException e) {204throw new RuntimeException("Unable to get Class object by its name either due to a different ClassLoader (a test bug) or JVM error", e);205}206}207208/**209* Launches MLVM test. The method is the principal MLVM test launcher. This method in conjunction with {@link #runMlvmTest} method:210* <ol>211* <li>instantiates test class (optionally passing arguments to constructor)212* <li>parses command-line arguments using vm.share.options framework and updates the appropriate test fields213* <li>launches the tests214* <li>handles all test status passing methods (see below)215* <li>performs System.exit() with VM-testbase standard exit code 95 (success) or 97 (failure).216* </ol>217*218* <p>The following tests status passing mechanism are available to test writer:219* <ol>220* <li>Return a boolean value (true if test passed, false otherwise. Good for simple tests)221* <li>Assume that test has failed by calling {@link MlvmTest#markTestFailed()} method (suitable for complex logic and multithreaded tests)222* <li>by throwing exception from test {@link MlvmTest#run()} method223* </ol>224225* <p>Additionally the launcher:226* <ul>227* <li>measures test run time and logs it228* <li>optionally dumps heap after test execution if {@link MlvmTest#setHeapDumpAfer(true)} was called229* <li>enables verbose logging on error230* </ul>231*232* @param testClass a class to instantiate. Has to be subclass of vm.mlvm.share.MlvmTest or java.lang.Runnable233* @param constructorArgs Arguments to pass to test constructor. Can be null.234* @see #runMlvmTest(Class<?> testClass, Object[] constructorArgs)235*/236public static void launch(Class<?> testClass, Object[] constructorArgs) {237Env.getLog().enableVerboseOnError(true);238239long startTime = System.currentTimeMillis();240boolean passed;241try {242Env.traceDebug(MlvmTest.getName() + " class: " + testClass);243passed = runMlvmTest(testClass, constructorArgs);244} catch (Throwable e) {245Env.complain(e, MlvmTest.getName() + " caught an exception: ");246passed = false;247}248249long finishTime = System.currentTimeMillis();250Env.traceNormal("The test took " + (finishTime - startTime) + " ms");251252optionallyDumpHeap();253254final String testNameUC = MlvmTest.getName().toUpperCase();255if (passed) {256Env.display(testNameUC + " PASSED");257System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_PASSED);258} else {259Env.display(testNameUC + " FAILED");260System.exit(Consts.JCK_STATUS_BASE + Consts.TEST_FAILED);261}262}263264private static void optionallyDumpHeap() {265try {266if (MlvmTest.getHeapDumpAfter()) {267ProcessUtils.dumpHeapWithHotspotDiagnosticMXBean(HEAP_DUMP_FILENAME);268}269} catch (Exception e) {270Env.traceNormal(e, "Error dumping heap: ");271}272}273274/**275* Launches MLVM test (syntatic shugar method).276* Calls {@link #runMlvmTest(Class<?> testClass, Object[] constructorArgs)} method providing no arguments to pass to the test constructor.277*278* The method throws unchecked exception when there is an error in test logic or handling.279* Exceptions thrown from MlvmTest.run() method or test constructor are wrapped into java.lang.RuntimeException or java.lang.Error280*281* @param testClass a class to instantiate. Has to be subclass of vm.mlvm.share.MlvmTest or java.lang.Runnable282* @return true if test passed, false otherwise283* @see #runMlvmTest(Class<?> testClass, Object[] constructorArgs)284*/285public static boolean runMlvmTest(Class<?> testClass) {286return runMlvmTest(testClass, null);287}288289/**290* Launches MLVM test. In details, it:291* <ol>292* <li>instantiates test class (optionally passing arguments to constructor)293* <li>parses command-line arguments using vm.share.options framework and updates the appropriate test fields294* <li>launches the tests295* <li>handles all test status passing methods (see below)296* </ol>297*298* <p>Unlike {@link #launch()} methods, it does NOT:299* <ul>300* <li>performs System.exit() with VM-testbase standard exit code 95 (success) or 97 (failure).301* <li>measures test run time and logs it302* <li>optionally dumps heap after test execution if {@link MlvmTest#setHeapDumpAfer(true)} was called303* <li>enables verbose logging on error304* </ul>305* Please see {@link #launch(Class<?> testClass, Object[] constructorArgs)} for additional details.306*307* The method throws unchecked exception when there is an error in test logic or handling.308* Exceptions thrown from MlvmTest.run() method or test constructor are wrapped into java.lang.RuntimeException or java.lang.Error309*310* @param testClass a class to instantiate. Has to be subclass of vm.mlvm.share.MlvmTest or java.lang.Runnable311* @param constructorArgs Arguments to pass to test constructor. Can be null.312* @return true if test passed, false otherwise313* @see #launch(Class<?> testClass, Object[] constructorArgs)314*/315public static boolean runMlvmTest(Class<?> testClass, Object[] constructorArgs) {316boolean passed;317Throwable exception = null;318319try {320MlvmTest instance = constructMlvmTest(testClass, constructorArgs);321setupMlvmTest(instance);322323instance.initializeTest();324325try {326passed = runMlvmTestInstance(instance);327} catch(Throwable e) {328exception = e;329passed = false;330}331332try {333instance.finalizeTest(passed);334} catch (Throwable e) {335Env.complain(e, "TEST BUG: exception thrown in finalizeTest");336if (exception == null) {337exception = e;338}339passed = false;340}341342} catch (Throwable e) {343Env.complain(e, "TEST BUG: exception thrown when instantiating/preparing test for run");344exception = e;345passed = false; // never really needed, but let's keep it346}347348if (exception != null) {349Env.throwAsUncheckedException(exception); // always throws350}351352return passed;353}354355private static void setupMlvmTest(MlvmTest instance) {356MlvmTest.setInstance(instance);357OptionSupport.setup(instance, Env.getArgParser().getRawArguments(), new IgnoreUnknownArgumentsHandler());358}359360private static boolean runMlvmTestInstance(MlvmTest instance) throws Throwable {361List<Class<? extends Throwable>> expectedExceptions = instance.getRequiredExceptions();362int runsCount = instance.getRunsNumber() * instance.getStressOptions().getRunsFactor();363if (runsCount < 1) {364throw new RuntimeException("Runs number obtained via command-line options is less than 1");365}366367int failedRuns = 0;368369try {370for (int run = 1; run <= runsCount; ++run) {371if (runsCount > 1) {372Env.traceNormal("TEST RUN " + run + "/" + runsCount + "; Failed " + failedRuns + " runs");373}374375if (run > 1) {376instance.resetTest();377}378379boolean instancePassed;380if (expectedExceptions.size() == 0) {381instancePassed = instance.run();382} else {383try {384instance.run();385Env.complain("Expected exceptions: " + expectedExceptions + ", but caught none");386instancePassed = false;387} catch (Throwable e) {388if (checkExpectedException(expectedExceptions, e)) {389instancePassed = true;390} else {391Env.complain(e, "Expected exceptions: " + expectedExceptions + ", but caught: ");392instancePassed = false;393}394}395}396397if (instance.isMarkedFailed()) {398instancePassed = false;399}400401if (!instancePassed) {402++failedRuns;403}404}405} finally {406if (failedRuns > 0) {407Env.complain("Failed runs: " + failedRuns + " of " + runsCount);408}409}410411return failedRuns == 0;412}413414private static Object constructTest(Class<?> testClass, Object[] constructorArgs) throws Throwable {415if (constructorArgs == null || constructorArgs.length == 0) {416return testClass.newInstance();417}418419for (Constructor<?> c : testClass.getConstructors()) {420Class<?> paramClasses[] = c.getParameterTypes();421if (!parametersAreAssignableFrom(paramClasses, constructorArgs)) {422continue;423}424425return c.newInstance(constructorArgs);426}427428throw new RuntimeException("Test bug: in class " + testClass.getName() + " no appropriate constructor found for arguments " + constructorArgs);429}430431private static MlvmTest constructMlvmTest(Class<?> testClass, Object[] constructorArgs) throws Throwable {432Object testObj = constructTest(testClass, constructorArgs);433434MlvmTest instance;435if (testObj instanceof MlvmTest) {436instance = (MlvmTest) testObj;437} else if (testObj instanceof Runnable) {438instance = new RunnableWrapper((Runnable) testObj);439} else {440// You can add wrapping of other types of tests here into MlvmTest if you need441throw new RuntimeException("TEST BUG: Test class should be subclass of MlvmTest or Runnable. Its type is "442+ testObj.getClass().getName());443}444445return instance;446}447448private static boolean parametersAreAssignableFrom(Class<?>[] paramClasses, Object[] constructorArgs) {449if (paramClasses.length != constructorArgs.length) {450return false;451}452453for (int i = 0; i < paramClasses.length; ++i) {454if (!paramClasses[i].isAssignableFrom(constructorArgs[i].getClass())) {455return false;456}457}458459return true;460}461462private static boolean checkExpectedException(List<Class<? extends Throwable>> expectedExceptions, Throwable caught) throws Throwable {463for (Class<? extends Throwable> expected : expectedExceptions) {464if (expected.isAssignableFrom(caught.getClass())) {465Env.traceNormal("Caught anticipated exception " + caught.getClass().getName() + ". Cause: " + caught.getCause());466return true;467}468}469470return false;471}472473private static class RunnableWrapper extends MlvmTest {474private Runnable runnable;475476public RunnableWrapper(Runnable r) {477runnable = r;478}479480@Override481public boolean run() throws Throwable {482runnable.run();483return true;484}485}486}487488489