1 /*
   2 1;2c * Copyright (c) 2013, 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.runtime.defmeth.shared;
  25 
  26 import java.lang.reflect.InvocationTargetException;
  27 import java.lang.reflect.Method;
  28 import java.lang.reflect.Modifier;
  29 import java.util.ArrayList;
  30 import java.util.List;
  31 import java.util.TreeSet;
  32 import java.util.regex.Pattern;
  33 import nsk.share.TestFailure;
  34 import nsk.share.log.Log;
  35 import nsk.share.test.TestBase;
  36 import vm.runtime.defmeth.AccessibilityFlagsTest;
  37 import vm.runtime.defmeth.BasicTest;
  38 import vm.runtime.defmeth.ConflictingDefaultsTest;
  39 import vm.runtime.defmeth.DefaultVsAbstractTest;
  40 import vm.runtime.defmeth.MethodResolutionTest;
  41 import vm.runtime.defmeth.ObjectMethodOverridesTest;
  42 import vm.runtime.defmeth.PrivateMethodsTest;
  43 import vm.runtime.defmeth.StaticMethodsTest;
  44 import vm.runtime.defmeth.SuperCallTest;
  45 import vm.runtime.defmeth.shared.annotation.Crash;
  46 import vm.runtime.defmeth.shared.annotation.KnownFailure;
  47 import vm.runtime.defmeth.shared.annotation.NotApplicableFor;
  48 import vm.runtime.defmeth.shared.builder.TestBuilderFactory;
  49 import vm.share.options.Option;
  50 import vm.share.options.OptionSupport;
  51 import vm.share.options.Options;
  52 import static java.lang.String.format;
  53 import java.util.Collections;
  54 import vm.runtime.defmeth.RedefineTest;
  55 import vm.runtime.defmeth.shared.annotation.NotTest;
  56 
  57 /**
  58  * Parent class for all default method tests.
  59  *
  60  * Contains common settings and code to run individual tests.
  61  *
  62  * Provides command-line interface to run arbitrary subset of
  63  * tests on default methods with some customizations.
  64  */
  65 public abstract class DefMethTest extends TestBase {
  66     /** Classes that contain tests on default methods */
  67     static private final List<Class<? extends DefMethTest>> classes;
  68 
  69     // the number of tests has failed
  70     // note that if more than one sub-test has failed within a test,
  71     // it will be counted as 1 failure for that test
  72     private int numFailures;
  73 
  74     static {
  75         List<Class<? extends DefMethTest>> intlList = new ArrayList<>();
  76 
  77         intlList.add(AccessibilityFlagsTest.class);
  78         intlList.add(BasicTest.class);
  79         intlList.add(ConflictingDefaultsTest.class);
  80         intlList.add(DefaultVsAbstractTest.class);
  81         intlList.add(MethodResolutionTest.class);
  82         intlList.add(ObjectMethodOverridesTest.class);
  83         intlList.add(SuperCallTest.class);
  84         intlList.add(PrivateMethodsTest.class);
  85         intlList.add(StaticMethodsTest.class);
  86         intlList.add(RedefineTest.class);
  87 
  88         classes = Collections.unmodifiableList(intlList);
  89     }
  90 
  91     public static List<Class<? extends DefMethTest>> getTests() {
  92         return classes;
  93     }
  94 
  95     @Option(name="list", default_value="false", description="list tests w/o executing them")
  96     boolean listTests;
  97 
  98     @Option(name="filter", default_value="", description="filter executed tests")
  99     String filterString;
 100 
 101     @Option(name="ignoreKnownFailures", default_value="false", description="ignore tests with known failures")
 102     boolean ignoreKnownFailures;
 103 
 104     @Option(name="runOnlyFailingTests", default_value="false", description="run only failing tests")
 105     boolean runOnlyFailingTests;
 106 
 107     @Option(name="ignoreCrashes", default_value="false", description="don't run tests with crash VM")
 108     boolean ignoreCrashes;
 109 
 110     @Option(name="silent", default_value="false", description="silent mode - don't print anything")
 111     boolean isSilent;
 112 
 113     @Option(name="failfast", default_value="false", description="fail the whole set of test on first failure")
 114     boolean failFast;
 115 
 116     @Option(name="testAllModes", default_value="false", description="run each test in all possible modes")
 117     boolean testAllModes;
 118 
 119     @Option(name="mode", description="invocation mode (direct, reflect, invoke)", default_value="direct")
 120     private String mode;
 121 
 122     private Pattern filter; // Precompiled pattern for filterString
 123 
 124     /**
 125      * Used from individual tests to get TestBuilder instances,
 126      * which is aware of current testing configuration
 127      */
 128     @Options
 129     protected TestBuilderFactory factory = new TestBuilderFactory(this);
 130 
 131     private void init() {
 132         if (isSilent) {
 133             getLog().setInfoEnabled(false);
 134             getLog().setWarnEnabled(false);
 135             getLog().setDebugEnabled(false);
 136         }
 137 
 138         if (filterString != null && !"".equals(filterString)) {
 139             filter = Pattern.compile(".*" + filterString + ".*");
 140         } else {
 141             filter = Pattern.compile(".*"); // match everything
 142         }
 143 
 144         // Test-specific config
 145         configure();
 146     }
 147 
 148     @Override
 149     public final void run() {
 150         init();
 151 
 152         boolean success = runTest();
 153         if (!success) {
 154             getLog().info("TEST FAILED");
 155             setFailed(true);
 156         } else {
 157             getLog().info("TEST PASSED");
 158         }
 159     }
 160 
 161     protected void configure() {
 162         // Is overriden by specific tests to do test-specific setup
 163     }
 164 
 165     public Log getLog() {
 166         return log;
 167     }
 168 
 169     @Override
 170     public String toString() {
 171         return format("%s%s",
 172                 getClass().getSimpleName(), factory);
 173     }
 174 
 175     /** Enumerate invocation modes to be tested */
 176     private ExecutionMode[] getInvocationModes() {
 177         if (factory.getExecutionMode() != null) {
 178             return new ExecutionMode[] { ExecutionMode.valueOf(factory.getExecutionMode()) };
 179         }
 180 
 181         if (testAllModes) {
 182             return ExecutionMode.values();
 183         }
 184 
 185         switch(mode) {
 186             case "direct":  return new ExecutionMode[] { ExecutionMode.DIRECT };
 187             case "reflect": return new ExecutionMode[] { ExecutionMode.REFLECTION };
 188             case "invoke_exact":   return new ExecutionMode[] { ExecutionMode.INVOKE_EXACT };
 189             case "invoke_generic": return new ExecutionMode[] { ExecutionMode.INVOKE_GENERIC };
 190             case "indy":    return new ExecutionMode[] { ExecutionMode.INDY };
 191             case "invoke":  return new ExecutionMode[] { ExecutionMode.INVOKE_WITH_ARGS,
 192                                                          ExecutionMode.INVOKE_EXACT,
 193                                                          ExecutionMode.INVOKE_GENERIC,
 194                                                          ExecutionMode.INDY };
 195             case "redefinition":
 196                 throw new Error("redefinition is only a pseudo-mode");
 197             default:
 198                 throw new Error("Unknown mode: " + mode);
 199         }
 200     }
 201 
 202     // Check whether the test is applicable to selected execution mode
 203     private boolean shouldExecute(Method m, ExecutionMode mode) {
 204         Class<? extends DefMethTest> test = this.getClass();
 205 
 206         int acc = m.getModifiers();
 207         if (m.isAnnotationPresent(NotTest.class)
 208                 || (ignoreCrashes && m.isAnnotationPresent(Crash.class))
 209                 || !Modifier.isPublic(acc) || Modifier.isStatic(acc)
 210                 //|| m.getReturnType() != Void.class
 211                 || m.getParameterTypes().length != 0)
 212         {
 213             return false; // not a test
 214         }
 215 
 216         String testName = format("%s.%s", test.getName(), m.getName());
 217         if (!filter.matcher(testName).matches()) {
 218             return false; // test is filtered out
 219         }
 220 
 221         if (m.isAnnotationPresent(NotApplicableFor.class)) {
 222             for (ExecutionMode excludeFromMode : m.getAnnotation(NotApplicableFor.class).modes()) {
 223                 if (mode == excludeFromMode) {
 224                     return false; // not applicable to current execution mode
 225                 } else if (excludeFromMode == ExecutionMode.REDEFINITION &&
 226                           (factory.isRedefineClasses() || factory.isRetransformClasses())) {
 227                     return false; // Can't redefine some tests.
 228                 }
 229 
 230             }
 231         }
 232 
 233           if (ignoreKnownFailures &&
 234             m.isAnnotationPresent(KnownFailure.class)) {
 235             ExecutionMode[] modes = m.getAnnotation(KnownFailure.class).modes();
 236 
 237             if (modes.length == 0)  return false; // by default, matches all modes
 238 
 239             for (ExecutionMode knownFailingMode : modes) {
 240                 if (mode == knownFailingMode) {
 241                     return false; // known failure in current mode
 242                 }
 243             }
 244         }
 245 
 246         return true;
 247     }
 248 
 249     /** Information about the test being executed */
 250     public String shortTestName;
 251 
 252     public static class ComparableMethod implements Comparable<ComparableMethod> {
 253         final java.lang.reflect.Method m;
 254         ComparableMethod(java.lang.reflect.Method m) { this.m = m; }
 255         public int compareTo(ComparableMethod mo) {
 256            String name = m.getName();
 257            String mo_name = mo.m.getName();
 258            return name.compareTo(mo_name);
 259         }
 260     }
 261 
 262     /** helper method for subclass to report the number of test failures.
 263      *  It is more important for the reflection case as an exception thrown
 264      *  deep in the call stack may not be propagated to this level.
 265      *
 266      * @param failures
 267      */
 268     public void addFailureCount(int failures) {
 269         numFailures += failures;
 270     }
 271 
 272     /**
 273      * Execute all tests from current class and report status.
 274      *
 275      * The following execution customization is supported:
 276      *   - filter tests by name using regex
 277      *   - ignore tests marked as @KnownFailure
 278      *   - ignore tests marked as @Crash
 279      *   - only run tests marked as @KnownFailure
 280      *
 281      * @return any failures occurred?
 282      */
 283     public final boolean runTest() {
 284         if (ignoreKnownFailures && runOnlyFailingTests) {
 285             throw new IllegalArgumentException("conflicting parameters");
 286         }
 287 
 288         ExecutionMode[] invocationModes = getInvocationModes();
 289 
 290         try {
 291             int totalTests = 0;
 292             int passedTests = 0;
 293 
 294             Class<? extends DefMethTest> test = this.getClass();
 295 
 296             getLog().info(format("\n%s %s", test.getSimpleName(), factory.toString()));
 297 
 298             TreeSet<ComparableMethod> ts = new TreeSet<ComparableMethod>();
 299             for (java.lang.reflect.Method m : test.getDeclaredMethods()) {
 300                 ts.add(new ComparableMethod(m));
 301             }
 302 
 303             for (ComparableMethod cm : ts) {
 304                 java.lang.reflect.Method m = cm.m;
 305                 for (ExecutionMode mode : invocationModes) {
 306                     shortTestName = format("%s.%s", test.getSimpleName(), m.getName());
 307 
 308                     if (!shouldExecute(m, mode)) {
 309                         continue; // skip the test due to current configuration
 310                     }
 311 
 312                     totalTests++;
 313 
 314                     getLog().info(shortTestName);
 315 
 316                     if (listTests) {
 317                         continue; // just print test info
 318                     }
 319 
 320                     // Iterate over all test modes
 321                     try {
 322                         factory.setExecutionMode(mode.name());
 323                         getLog().info(format("    %s: ", mode));
 324                         m.invoke(this);
 325                     } catch (IllegalAccessException | IllegalArgumentException e) {
 326                         throw new TestFailure(e);
 327                     } catch (InvocationTargetException e) {
 328                         if (e.getCause() instanceof TestFailure) {
 329                             // Failure details were printed in GeneratedTest.run()/ReflectionTest.run()
 330                         } else {
 331                             if (Constants.PRINT_STACK_TRACE) {
 332                                 e.printStackTrace();
 333                             }
 334                         }
 335 
 336                         if (failFast) {
 337                             throw new TestFailure(e.getCause());
 338                         }
 339                     }
 340                 }
 341             }
 342 
 343             passedTests = totalTests - numFailures;
 344             getLog().info(format("%d test run: %d passed, %d failed", totalTests, passedTests, numFailures));
 345             if (numFailures == 0) {
 346                 return true;
 347             } else {
 348                 return false;
 349             }
 350         } catch (Exception | Error e) {
 351             throw new RuntimeException(e);
 352         }
 353     }
 354 
 355     /** Command-line interface to initiate test run */
 356     public static void main(String[] args) {
 357         for (Class<? extends DefMethTest> clz : classes) {
 358             try {
 359                 DefMethTest test = clz.newInstance();
 360                 OptionSupport.setupAndRun(test, args);
 361             } catch (InstantiationException | IllegalAccessException e) {
 362                 throw new TestFailure(e);
 363             }
 364         }
 365     }
 366 }