/* * 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(); } }