1 /*
2  * 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 
getTests()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 
init()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
run()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 
configure()161     protected void configure() {
162         // Is overriden by specific tests to do test-specific setup
163     }
164 
getLog()165     public Log getLog() {
166         return log;
167     }
168 
169     @Override
toString()170     public String toString() {
171         return format("%s%s",
172                 getClass().getSimpleName(), factory);
173     }
174 
175     /** Enumerate invocation modes to be tested */
getInvocationModes()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
shouldExecute(Method m, ExecutionMode 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;
ComparableMethod(java.lang.reflect.Method m)254         ComparableMethod(java.lang.reflect.Method m) { this.m = m; }
compareTo(ComparableMethod mo)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      */
addFailureCount(int failures)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      */
runTest()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                         addFailureCount(1);
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 */
main(String[] args)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 }
367