308 lines
11 KiB
Java
308 lines
11 KiB
Java
|
/*
|
||
|
* Copyright (c) 2013, 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.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* @test
|
||
|
* @summary Test locating and invoking default/static method that defined
|
||
|
* in interfaces and/or in inheritance
|
||
|
* @bug 7184826
|
||
|
* @build helper.Mod helper.Declared DefaultStaticTestData
|
||
|
* @run testng DefaultStaticInvokeTest
|
||
|
* @author Yong Lu
|
||
|
*/
|
||
|
|
||
|
import java.lang.invoke.MethodHandle;
|
||
|
import java.lang.invoke.MethodHandles;
|
||
|
import java.lang.invoke.MethodType;
|
||
|
import java.lang.reflect.Method;
|
||
|
import java.lang.reflect.Modifier;
|
||
|
import java.util.Arrays;
|
||
|
import java.util.HashMap;
|
||
|
import java.util.HashSet;
|
||
|
|
||
|
import static org.testng.Assert.assertEquals;
|
||
|
import static org.testng.Assert.assertTrue;
|
||
|
import static org.testng.Assert.assertFalse;
|
||
|
import static org.testng.Assert.assertNotNull;
|
||
|
import org.testng.annotations.Test;
|
||
|
|
||
|
import static helper.Mod.*;
|
||
|
import static helper.Declared.*;
|
||
|
import helper.Mod;
|
||
|
|
||
|
public class DefaultStaticInvokeTest {
|
||
|
|
||
|
@Test(dataProvider = "testCasesAll",
|
||
|
dataProviderClass = DefaultStaticTestData.class)
|
||
|
public void testGetMethods(String testTarget, Object param)
|
||
|
throws Exception {
|
||
|
// test the methods retrieved by getMethods()
|
||
|
testMethods(ALL_METHODS, testTarget, param);
|
||
|
}
|
||
|
|
||
|
@Test(dataProvider = "testCasesAll",
|
||
|
dataProviderClass = DefaultStaticTestData.class)
|
||
|
public void testGetDeclaredMethods(String testTarget, Object param)
|
||
|
throws Exception {
|
||
|
// test the methods retrieved by getDeclaredMethods()
|
||
|
testMethods(DECLARED_ONLY, testTarget, param);
|
||
|
}
|
||
|
|
||
|
@Test(dataProvider = "testCasesAll",
|
||
|
dataProviderClass = DefaultStaticTestData.class)
|
||
|
public void testMethodInvoke(String testTarget, Object param)
|
||
|
throws Exception {
|
||
|
Class<?> typeUnderTest = Class.forName(testTarget);
|
||
|
MethodDesc[] expectedMethods = typeUnderTest.getAnnotationsByType(MethodDesc.class);
|
||
|
|
||
|
// test the method retrieved by Class.getMethod(String, Object[])
|
||
|
for (MethodDesc toTest : expectedMethods) {
|
||
|
String name = toTest.name();
|
||
|
Method m = getTestMethod(typeUnderTest, name, param);
|
||
|
testThisMethod(toTest, m, typeUnderTest, param);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Test(dataProvider = "testCasesAll",
|
||
|
dataProviderClass = DefaultStaticTestData.class)
|
||
|
public void testMethodHandleInvoke(String testTarget, Object param)
|
||
|
throws Throwable {
|
||
|
Class<?> typeUnderTest = Class.forName(testTarget);
|
||
|
MethodDesc[] expectedMethods = typeUnderTest.getAnnotationsByType(MethodDesc.class);
|
||
|
|
||
|
for (MethodDesc toTest : expectedMethods) {
|
||
|
String mName = toTest.name();
|
||
|
Mod mod = toTest.mod();
|
||
|
if (mod != STATIC && typeUnderTest.isInterface()) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
String result = null;
|
||
|
String expectedReturn = toTest.retval();
|
||
|
|
||
|
MethodHandle methodHandle = getTestMH(typeUnderTest, mName, param);
|
||
|
if (mName.equals("staticMethod")) {
|
||
|
result = (param == null)
|
||
|
? (String) methodHandle.invoke()
|
||
|
: (String) methodHandle.invoke(param);
|
||
|
} else {
|
||
|
result = (param == null)
|
||
|
? (String) methodHandle.invoke(typeUnderTest.newInstance())
|
||
|
: (String) methodHandle.invoke(typeUnderTest.newInstance(), param);
|
||
|
}
|
||
|
|
||
|
assertEquals(result, expectedReturn);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
@Test(dataProvider = "testClasses",
|
||
|
dataProviderClass = DefaultStaticTestData.class)
|
||
|
public void testIAE(String testTarget, Object param)
|
||
|
throws ClassNotFoundException {
|
||
|
|
||
|
Class<?> typeUnderTest = Class.forName(testTarget);
|
||
|
MethodDesc[] expectedMethods = typeUnderTest.getAnnotationsByType(MethodDesc.class);
|
||
|
|
||
|
for (MethodDesc toTest : expectedMethods) {
|
||
|
String mName = toTest.name();
|
||
|
Mod mod = toTest.mod();
|
||
|
if (mod != STATIC && typeUnderTest.isInterface()) {
|
||
|
return;
|
||
|
}
|
||
|
Exception caught = null;
|
||
|
try {
|
||
|
getTestMH(typeUnderTest, mName, param, true);
|
||
|
} catch (Exception e) {
|
||
|
caught = e;
|
||
|
}
|
||
|
assertTrue(caught != null);
|
||
|
assertEquals(caught.getClass(), IllegalAccessException.class);
|
||
|
}
|
||
|
}
|
||
|
private static final String[] OBJECT_METHOD_NAMES = {
|
||
|
"equals",
|
||
|
"hashCode",
|
||
|
"getClass",
|
||
|
"notify",
|
||
|
"notifyAll",
|
||
|
"toString",
|
||
|
"wait",
|
||
|
"wait",
|
||
|
"wait",};
|
||
|
private static final String LAMBDA_METHOD_NAMES = "lambda$";
|
||
|
private static final HashSet<String> OBJECT_NAMES = new HashSet<>(Arrays.asList(OBJECT_METHOD_NAMES));
|
||
|
private static final boolean DECLARED_ONLY = true;
|
||
|
private static final boolean ALL_METHODS = false;
|
||
|
|
||
|
private void testMethods(boolean declaredOnly, String testTarget, Object param)
|
||
|
throws Exception {
|
||
|
Class<?> typeUnderTest = Class.forName(testTarget);
|
||
|
Method[] methods = declaredOnly
|
||
|
? typeUnderTest.getDeclaredMethods()
|
||
|
: typeUnderTest.getMethods();
|
||
|
|
||
|
MethodDesc[] baseExpectedMethods = typeUnderTest.getAnnotationsByType(MethodDesc.class);
|
||
|
MethodDesc[] expectedMethods;
|
||
|
|
||
|
// If only declared filter out non-declared from expected result
|
||
|
if (declaredOnly) {
|
||
|
int nonDeclared = 0;
|
||
|
for (MethodDesc desc : baseExpectedMethods) {
|
||
|
if (desc.declared() == NO) {
|
||
|
nonDeclared++;
|
||
|
}
|
||
|
}
|
||
|
expectedMethods = new MethodDesc[baseExpectedMethods.length - nonDeclared];
|
||
|
int i = 0;
|
||
|
for (MethodDesc desc : baseExpectedMethods) {
|
||
|
if (desc.declared() == YES) {
|
||
|
expectedMethods[i++] = desc;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
expectedMethods = baseExpectedMethods;
|
||
|
}
|
||
|
|
||
|
HashMap<String, Method> myMethods = new HashMap<>(methods.length);
|
||
|
for (Method m : methods) {
|
||
|
String mName = m.getName();
|
||
|
// don't add Object methods and method created from lambda expression
|
||
|
if ((!OBJECT_NAMES.contains(mName)) && (!mName.contains(LAMBDA_METHOD_NAMES))) {
|
||
|
myMethods.put(mName, m);
|
||
|
}
|
||
|
}
|
||
|
assertEquals(expectedMethods.length, myMethods.size());
|
||
|
|
||
|
for (MethodDesc toTest : expectedMethods) {
|
||
|
|
||
|
String name = toTest.name();
|
||
|
Method candidate = myMethods.get(name);
|
||
|
|
||
|
assertNotNull(candidate);
|
||
|
myMethods.remove(name);
|
||
|
|
||
|
testThisMethod(toTest, candidate, typeUnderTest, param);
|
||
|
|
||
|
}
|
||
|
|
||
|
// Should be no methods left since we remove all we expect to see
|
||
|
assertTrue(myMethods.isEmpty());
|
||
|
}
|
||
|
|
||
|
private void testThisMethod(MethodDesc toTest, Method method,
|
||
|
Class<?> typeUnderTest, Object param) throws Exception {
|
||
|
// Test modifiers, and invoke
|
||
|
Mod mod = toTest.mod();
|
||
|
String expectedReturn = toTest.retval();
|
||
|
switch (mod) {
|
||
|
case STATIC:
|
||
|
//assert candidate is static
|
||
|
assertTrue(Modifier.isStatic(method.getModifiers()));
|
||
|
assertFalse(method.isDefault());
|
||
|
|
||
|
// Test invoke it
|
||
|
assertEquals(tryInvoke(method, null, param), expectedReturn);
|
||
|
break;
|
||
|
case DEFAULT:
|
||
|
// if typeUnderTest is a class then instantiate and invoke
|
||
|
if (!typeUnderTest.isInterface()) {
|
||
|
assertEquals(tryInvoke(
|
||
|
method,
|
||
|
typeUnderTest,
|
||
|
param),
|
||
|
expectedReturn);
|
||
|
}
|
||
|
|
||
|
//assert candidate is default
|
||
|
assertFalse(Modifier.isStatic(method.getModifiers()));
|
||
|
assertTrue(method.isDefault());
|
||
|
break;
|
||
|
case REGULAR:
|
||
|
// if typeUnderTest must be a class
|
||
|
assertEquals(tryInvoke(
|
||
|
method,
|
||
|
typeUnderTest,
|
||
|
param),
|
||
|
expectedReturn);
|
||
|
|
||
|
//assert candidate is neither default nor static
|
||
|
assertFalse(Modifier.isStatic(method.getModifiers()));
|
||
|
assertFalse(method.isDefault());
|
||
|
break;
|
||
|
case ABSTRACT:
|
||
|
//assert candidate is neither default nor static
|
||
|
assertFalse(Modifier.isStatic(method.getModifiers()));
|
||
|
assertFalse(method.isDefault());
|
||
|
break;
|
||
|
default:
|
||
|
assertFalse(true); //this should never happen
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
private Object tryInvoke(Method m, Class<?> receiverType, Object param)
|
||
|
throws Exception {
|
||
|
Object receiver = receiverType == null ? null : receiverType.newInstance();
|
||
|
Object result = null;
|
||
|
if (param == null) {
|
||
|
result = m.invoke(receiver);
|
||
|
} else {
|
||
|
result = m.invoke(receiver, param);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
private Method getTestMethod(Class clazz, String methodName, Object param)
|
||
|
throws NoSuchMethodException {
|
||
|
Class[] paramsType = (param != null)
|
||
|
? new Class[]{Object.class}
|
||
|
: new Class[]{};
|
||
|
return clazz.getMethod(methodName, paramsType);
|
||
|
}
|
||
|
|
||
|
private MethodHandle getTestMH(Class clazz, String methodName, Object param)
|
||
|
throws Exception {
|
||
|
return getTestMH(clazz, methodName, param, false);
|
||
|
}
|
||
|
|
||
|
private MethodHandle getTestMH(Class clazz, String methodName,
|
||
|
Object param, boolean isNegativeTest)
|
||
|
throws Exception {
|
||
|
MethodType mType = (param != null)
|
||
|
? MethodType.genericMethodType(1)
|
||
|
: MethodType.methodType(String.class);
|
||
|
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||
|
if (!isNegativeTest) {
|
||
|
return methodName.equals("staticMethod")
|
||
|
? lookup.findStatic(clazz, methodName, mType)
|
||
|
: lookup.findVirtual(clazz, methodName, mType);
|
||
|
} else {
|
||
|
return methodName.equals("staticMethod")
|
||
|
? lookup.findVirtual(clazz, methodName, mType)
|
||
|
: lookup.findStatic(clazz, methodName, mType);
|
||
|
}
|
||
|
}
|
||
|
}
|