--- /dev/null 2014-11-30 10:11:31.639805301 +0100 +++ new/test/java/lang/Class/getMethods/MethodTableTest.java 2014-11-30 18:51:42.894533155 +0100 @@ -0,0 +1,352 @@ +/* + * Copyright (c) 1994, 2014, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +import jdk.testlibrary.TestProxy; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.function.Function; + +/** + * @test + * @summary Unit tests for java.lang.MethodTable internal implementations. + */ +public class MethodTableTest { + + public static void main(String[] args) { + + testAdd(MethodTable::newSimpleArrayImpl); + testAddUnlessDeclaredExists(MethodTable::newSimpleArrayImpl); + testConsolidate(MethodTable::newSimpleArrayImpl); + testGetFirstMethodWithMostSpecificReturnType(MethodTable::newSimpleArrayImpl); + + testAdd(MethodTable::newHashArrayImpl); + testAddUnlessDeclaredExists(MethodTable::newHashArrayImpl); + testConsolidate(MethodTable::newHashArrayImpl); + testGetFirstMethodWithMostSpecificReturnType(MethodTable::newHashArrayImpl); + + System.out.println("All tests passed."); + } + + // MethodTable.add + + static void testAdd(Method[] methods, + Function mtFactory) { + MethodTable mt = mtFactory.apply(methods.length); + assertEmpty(mt); + for (Method m : methods) mt.add(m); + assertSameObjects(methods, mt.getMethods()); + } + + static void testAdd(Function mtFactory) { + Method[] methods = A.class.getDeclaredMethods(); + + testAdd(methods, mtFactory); + + // repeated sequence + Method[] methods2 = concat(methods, methods); + + testAdd(methods2, mtFactory); + } + + // MethodTable.addUnlessDeclaredExists + + static void testAddUnlessDeclaredExists(Method[] methods, + Class declaringClass, + Method[] expectedResult, + Function mtFactory) { + MethodTable mt = mtFactory.apply(methods.length); + assertEmpty(mt); + for (Method m : methods) mt.addUnlessDeclaredExists(m, declaringClass); + assertSameObjects(expectedResult, mt.getMethods()); + } + + static void testAddUnlessDeclaredExists(Function mtFactory) { + Method[] methodsA = A.class.getDeclaredMethods(); + Method[] methodsB = B.class.getDeclaredMethods(); + + testAddUnlessDeclaredExists(methodsA, A.class, methodsA, mtFactory); + + // concatenated methods + Method[] methodsAB = concat(methodsA, methodsB); + Method[] methodsBA = concat(methodsB, methodsA); + + testAddUnlessDeclaredExists(methodsAB, Object.class, methodsAB, mtFactory); + testAddUnlessDeclaredExists(methodsAB, A.class, methodsA, mtFactory); + testAddUnlessDeclaredExists(methodsAB, B.class, methodsAB, mtFactory); + + testAddUnlessDeclaredExists(methodsBA, Object.class, methodsBA, mtFactory); + testAddUnlessDeclaredExists(methodsBA, A.class, methodsBA, mtFactory); + testAddUnlessDeclaredExists(methodsBA, B.class, methodsB, mtFactory); + } + + // MethodTable.consolidate + + static void testConsolidate(Method[] methods, + Class declaringClass, + Method[] expectedResult, + Function mtFactory) { + MethodTable mt = mtFactory.apply(methods.length); + assertEmpty(mt); + for (Method m : methods) mt.consolidate(m, declaringClass); + assertSameObjects(expectedResult, mt.getMethods()); + } + + static void testConsolidate(Function mtFactory) { + Method[] methodsA = A.class.getDeclaredMethods(); + Method[] methodsB = B.class.getDeclaredMethods(); + Method[] methodsAX = AX.class.getDeclaredMethods(); + Method[] methodsI = I.class.getDeclaredMethods(); + Method[] methodsX = X.class.getDeclaredMethods(); + Method[] methodsY = Y.class.getDeclaredMethods(); + Method[] methodsZ = Z.class.getDeclaredMethods(); + + testConsolidate(methodsA, A.class, methodsA, mtFactory); + + // if given method is same method as existing method, + // the operation completes without adding given method. + testConsolidate(concat(methodsA, methodsA), Object.class, methodsA, mtFactory); + + // concatenated methods + Method[] methodsAB = concat(methodsA, methodsB); + Method[] methodsBA = concat(methodsB, methodsA); + + // if existing method is declared by given declaringClass, + // the operation completes without adding given method. + testConsolidate(methodsAB, A.class, methodsA, mtFactory); + testConsolidate(methodsBA, B.class, methodsB, mtFactory); + testConsolidate(methodsAB, Object.class, methodsAB, mtFactory); + testConsolidate(methodsBA, Object.class, methodsBA, mtFactory); + + // if existing method is not abstract and either existing method + // overrides given method... + testConsolidate(concat(methodsAX, methodsA), Object.class, methodsAX, mtFactory); + + // ...or existing method is declared by class + // and given method is declared by interface, the operation completes + // without adding given method. + testConsolidate(concat(methodsA, methodsI), Object.class, methodsA, mtFactory); + + // if given method is not abstract and either given method overrides existing + // method... + testConsolidate(concat(methodsA, methodsAX), Object.class, methodsAX, mtFactory); + + // ...or given method is declared by class and existing method is declared + // by interface, then existing method is removed from this MethodTable... + testConsolidate(concat(methodsI, methodsA), Object.class, methodsA, mtFactory); + + // ...if there are no more existing methods then given method is added to this + // MethodTable and operation completes else next existing method is taken + // into consideration. + testConsolidate(concat(methodsX, methodsY, methodsZ), Object.class, + concat(methodsX, methodsY, methodsZ), mtFactory); + } + + // + + static void testGetFirstMethodWithMostSpecificReturnType(Method[] methods, + Method expectedResult, + Function mtFactory) { + MethodTable mt = mtFactory.apply(methods.length); + assertEmpty(mt); + for (Method m : methods) mt.add(m); + assertSameObject(expectedResult, mt.getFirstMethodWithMostSpecificReturnType()); + } + + static void testGetFirstMethodWithMostSpecificReturnType(Function mtFactory) { + Method[] methodsX = X.class.getDeclaredMethods(); + Method[] methodsY = Y.class.getDeclaredMethods(); + Method[] methodsZ = Z.class.getDeclaredMethods(); + + testGetFirstMethodWithMostSpecificReturnType(methodsX, methodsX[0], mtFactory); + + testGetFirstMethodWithMostSpecificReturnType(concat(methodsX, methodsY), methodsY[0], mtFactory); + testGetFirstMethodWithMostSpecificReturnType(concat(methodsY, methodsX), methodsY[0], mtFactory); + testGetFirstMethodWithMostSpecificReturnType(concat(methodsY, methodsZ), methodsY[0], mtFactory); + testGetFirstMethodWithMostSpecificReturnType(concat(methodsZ, methodsY), methodsZ[0], mtFactory); + testGetFirstMethodWithMostSpecificReturnType(concat(methodsX, methodsZ), methodsZ[0], mtFactory); + testGetFirstMethodWithMostSpecificReturnType(concat(methodsZ, methodsX), methodsZ[0], mtFactory); + + testGetFirstMethodWithMostSpecificReturnType(concat(methodsX, methodsY, methodsZ), methodsY[0], mtFactory); + testGetFirstMethodWithMostSpecificReturnType(concat(methodsX, methodsZ, methodsY), methodsZ[0], mtFactory); + testGetFirstMethodWithMostSpecificReturnType(concat(methodsY, methodsX, methodsZ), methodsY[0], mtFactory); + testGetFirstMethodWithMostSpecificReturnType(concat(methodsZ, methodsX, methodsY), methodsZ[0], mtFactory); + testGetFirstMethodWithMostSpecificReturnType(concat(methodsY, methodsZ, methodsX), methodsY[0], mtFactory); + testGetFirstMethodWithMostSpecificReturnType(concat(methodsZ, methodsY, methodsX), methodsZ[0], mtFactory); + } + + // test infrastructure + + static void assertEmpty(MethodTable mt) { + if (mt.getSize() != 0) { + throw new AssertionError("MethodTable is not empty"); + } + } + + static void assertSameObject(Object expected, Object actual) { + if (expected != actual) { + throw new AssertionError("Expected:\n " + expected + + "\nActual:\n " + actual); + + } + } + + static void assertSameObjects(Object[] expected, Object[] actual) { + boolean ok = (expected.length == actual.length); + if (ok) { + for (int i = 0; ok && i < expected.length; i++) { + ok = (expected[i] == actual[i]); + } + } + if (!ok) { + throw new AssertionError("Expected:\n " + Arrays.toString(expected) + + "\nActual:\n " + Arrays.toString(actual)); + } + } + + static Method[] concat(Method[]... mas) { + int len = 0; + for (Method[] ma : mas) { + len += ma.length; + } + Method[] cma = new Method[len]; + int offset = 0; + for (Method[] ma : mas) { + System.arraycopy(ma, 0, cma, offset, ma.length); + offset += ma.length; + } + return cma; + } + + static class A { + public void m() { + } + + public void m(int i) { + } + + public void m(long l) { + } + + public void m(Object o) { + } + + public void m(String s) { + } + } + + static class AX extends A { + public void m() { + } + + public void m(int i) { + } + + public void m(long l) { + } + + public void m(Object o) { + } + + public void m(String s) { + } + } + + static class B { + public void m() { + } + + public void m(int i) { + } + + public void m(long l) { + } + + public void m(Object o) { + } + + public void m(String s) { + } + } + + interface I { + void m(); + + void m(int i); + + void m(long l); + + void m(Object o); + + void m(String s); + } + + interface X { + Object m(); + } + + static abstract class Y { + public abstract Number m(); + } + + interface Z { + Number m(); + } + + // test proxy for package-private java.lang.MethodTable implementations + + interface MethodTable extends Iterable { + + static MethodTable newSimpleArrayImpl(int maxExpectedSize) { + return TestProxy.newInstance(MethodTable.class, + "java.lang.MethodTable$SimpleArrayImpl", + ClassLoader.getSystemClassLoader(), + new Class[]{int.class}, + maxExpectedSize); + } + + static MethodTable newHashArrayImpl(int maxExpectedSize) { + return TestProxy.newInstance(MethodTable.class, + "java.lang.MethodTable$HashArrayImpl", + ClassLoader.getSystemClassLoader(), + new Class[]{int.class}, + maxExpectedSize); + } + + void add(Method method); + + void addUnlessDeclaredExists(Method method, Class declaringClass); + + void consolidate(Method method, Class declaringClass); + + int getSize(); + + void clear(); + + Method[] getMethods(); + + Method getFirstMethodWithMostSpecificReturnType(); + } +}