| /* | |
| * Copyright (c) 2020, 2022, 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 | |
| * @bug 8227046 | |
| * @summary reflection test for sealed classes | |
| * @compile SealedClassesReflectionTest.java | |
| * @run testng/othervm SealedClassesReflectionTest | |
| */ | |
| import java.lang.annotation.*; | |
| import java.lang.constant.ClassDesc; | |
| import java.lang.reflect.*; | |
| import java.util.Arrays; | |
| import java.util.List; | |
| import org.testng.annotations.*; | |
| import static org.testng.Assert.*; | |
| @Test | |
| public class SealedClassesReflectionTest { | |
| sealed class SealedClass1 permits FinalSubclass1, NonSealedSubclass1 {} | |
| final class FinalSubclass1 extends SealedClass1 {} | |
| non-sealed class NonSealedSubclass1 extends SealedClass1 {} | |
| sealed class SealedClass2 permits FinalSubclass2, FinalSubclass3 {} | |
| final class FinalSubclass2 extends SealedClass2 {} | |
| final class FinalSubclass3 extends SealedClass2 {} | |
| sealed class SealedClass3 permits FinalSubclass4, SealedSubclass1 {} | |
| final class FinalSubclass4 extends SealedClass3 {} | |
| sealed class SealedSubclass1 extends SealedClass3 permits FinalSubclass5 {} | |
| final class FinalSubclass5 extends SealedSubclass1 {} | |
| sealed interface SealedInt1 permits FinalSubclass6, NonSealedSubInt1 {} | |
| final class FinalSubclass6 implements SealedInt1 {} | |
| non-sealed interface NonSealedSubInt1 extends SealedInt1 {} | |
| sealed interface SealedInt2 permits FinalSubclass7, FinalSubclass8 {} | |
| final class FinalSubclass7 implements SealedInt2 {} | |
| final class FinalSubclass8 implements SealedInt2 {} | |
| sealed interface SealedInt3 permits FinalSubclass9, SealedSubInt1, SealedSubclass2 {} | |
| final class FinalSubclass9 implements SealedInt3 {} | |
| sealed interface SealedSubInt1 extends SealedInt3 permits FinalSubclass10 {} | |
| final class FinalSubclass10 implements SealedSubInt1 {} | |
| sealed class SealedSubclass2 implements SealedInt3 permits NonSealedSubclass2 {} | |
| non-sealed class NonSealedSubclass2 extends SealedSubclass2 {} | |
| @DataProvider(name = "sealedClasses") | |
| public Object[][] sealedClassesData() { | |
| return List.of( | |
| SealedClass1.class, | |
| SealedClass2.class, | |
| SealedClass3.class, | |
| SealedSubclass1.class, | |
| SealedInt1.class, | |
| SealedInt2.class, | |
| SealedInt3.class, | |
| SealedSubInt1.class | |
| ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); | |
| } | |
| @Test(dataProvider = "sealedClasses") | |
| public void testSealedClasses(Class<?> cls) { | |
| assertTrue(cls.isSealed()); | |
| assertTrue(!Modifier.isFinal(cls.getModifiers())); | |
| assertTrue(cls.getPermittedSubclasses() != null); | |
| assertTrue(cls.getPermittedSubclasses().length > 0); | |
| } | |
| @DataProvider(name = "notSealedClasses") | |
| public Object[][] notSealedClassesData() { | |
| return List.of( | |
| Object.class, | |
| void.class, Void.class, Void[].class, | |
| byte.class, byte[].class, Byte.class, Byte[].class, | |
| short.class, short[].class, Short.class, Short[].class, | |
| char.class, char[].class, Character.class, Character[].class, | |
| int.class, int[].class, Integer.class, Integer[].class, | |
| long.class, long[].class, Long.class, Long[].class, | |
| float.class, float[].class, Float.class, Float[].class, | |
| double.class, double[].class, Double.class, Double[].class, | |
| boolean.class, boolean[].class, Boolean.class, Boolean[].class, | |
| String.class, String[].class | |
| ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); | |
| } | |
| @Test(dataProvider = "notSealedClasses") | |
| public void testNotSealedClasses(Class<?> cls) { | |
| assertTrue(!cls.isSealed()); | |
| assertTrue(cls.getPermittedSubclasses() == null); | |
| } | |
| @DataProvider(name = "non_sealedClasses") | |
| public Object[][] non_sealedClassesData() { | |
| return List.of( | |
| NonSealedSubclass1.class, | |
| NonSealedSubInt1.class, | |
| NonSealedSubclass2.class | |
| ).stream().map(c -> new Object[] {c}).toArray(Object[][]::new); | |
| } | |
| @Test(dataProvider = "non_sealedClasses") | |
| public void testnon_sealedClasses(Class<?> cls) { | |
| assertTrue(!cls.isSealed()); | |
| assertTrue(!Modifier.isFinal(cls.getModifiers())); | |
| assertTrue((cls.getSuperclass() != null && cls.getSuperclass().isSealed()) || Arrays.stream(cls.getInterfaces()).anyMatch(Class::isSealed)); | |
| assertTrue(cls.getPermittedSubclasses() == null); | |
| } | |
| @DataProvider(name = "reflectionData") | |
| public Object[][] reflectionData() { | |
| return new Object[][] { | |
| new Object[] { | |
| SealedClass1.class, | |
| 2, | |
| new String[] {"SealedClassesReflectionTest$FinalSubclass1", "SealedClassesReflectionTest$NonSealedSubclass1"}, | |
| new Class<?>[] {FinalSubclass1.class, NonSealedSubclass1.class}, | |
| new SealedStatus[] {SealedStatus.FINAL, SealedStatus.NON_SEALED}}, | |
| new Object[] { | |
| SealedClass2.class, | |
| 2, | |
| new String[] {"SealedClassesReflectionTest$FinalSubclass2", "SealedClassesReflectionTest$FinalSubclass3"}, | |
| new Class<?>[] {FinalSubclass2.class, FinalSubclass3.class}, | |
| new SealedStatus[] {SealedStatus.FINAL, SealedStatus.FINAL}}, | |
| new Object[] { | |
| SealedClass3.class, | |
| 2, | |
| new String[] {"SealedClassesReflectionTest$FinalSubclass4", "SealedClassesReflectionTest$SealedSubclass1"}, | |
| new Class<?>[] {FinalSubclass4.class, SealedSubclass1.class}, | |
| new SealedStatus[] {SealedStatus.FINAL, SealedStatus.SEALED}}, | |
| new Object[] { | |
| SealedSubclass1.class, | |
| 1, | |
| new String[] {"SealedClassesReflectionTest$FinalSubclass5"}, | |
| new Class<?>[] {FinalSubclass5.class}, | |
| new SealedStatus[] {SealedStatus.FINAL}}, | |
| new Object[] { | |
| SealedInt1.class, | |
| 2, | |
| new String[] {"SealedClassesReflectionTest$FinalSubclass6", "SealedClassesReflectionTest$NonSealedSubInt1"}, | |
| new Class<?>[] {FinalSubclass6.class, NonSealedSubInt1.class}, | |
| new SealedStatus[] {SealedStatus.FINAL, SealedStatus.NON_SEALED}}, | |
| new Object[] { | |
| SealedInt2.class, | |
| 2, | |
| new String[] {"SealedClassesReflectionTest$FinalSubclass7", "SealedClassesReflectionTest$FinalSubclass8"}, | |
| new Class<?>[] {FinalSubclass7.class, FinalSubclass8.class}, | |
| new SealedStatus[] {SealedStatus.FINAL, SealedStatus.FINAL}}, | |
| new Object[] { | |
| SealedInt3.class, | |
| 3, | |
| new String[] {"SealedClassesReflectionTest$FinalSubclass9", | |
| "SealedClassesReflectionTest$SealedSubInt1", | |
| "SealedClassesReflectionTest$SealedSubclass2"}, | |
| new Class<?>[] {FinalSubclass9.class, SealedSubInt1.class, SealedSubclass2.class}, | |
| new SealedStatus[] {SealedStatus.FINAL, SealedStatus.SEALED, SealedStatus.SEALED}}, | |
| new Object[] { | |
| SealedSubInt1.class, | |
| 1, | |
| new String[] {"SealedClassesReflectionTest$FinalSubclass10"}, | |
| new Class<?>[] {FinalSubclass10.class}, | |
| new SealedStatus[] {SealedStatus.FINAL}}, | |
| new Object[] { | |
| SealedSubclass2.class, | |
| 1, | |
| new String[] {"SealedClassesReflectionTest$NonSealedSubclass2"}, | |
| new Class<?>[] {NonSealedSubclass2.class}, | |
| new SealedStatus[] {SealedStatus.NON_SEALED}}, | |
| }; | |
| } | |
| enum SealedStatus { | |
| SEALED, NON_SEALED, FINAL | |
| } | |
| @Test(dataProvider = "reflectionData") | |
| public void testSealedReflection(Class<?> sealedClass, | |
| int numberOfSubclasses, | |
| String[] subclassDescriptors, | |
| Class<?>[] subclasses, | |
| SealedStatus[] subclassSealedStatus) | |
| throws ReflectiveOperationException | |
| { | |
| assertTrue(sealedClass.isSealed()); | |
| assertTrue(sealedClass.getPermittedSubclasses().length == numberOfSubclasses); | |
| int i = 0; | |
| for (Class<?> cd : sealedClass.getPermittedSubclasses()) { | |
| assertTrue(cd.getName().equals(subclassDescriptors[i]), "expected: " + subclassDescriptors[i] + " found: " + cd.getName()); | |
| i++; | |
| } | |
| i = 0; | |
| for (Class<?> subclass : subclasses) { | |
| switch (subclassSealedStatus[i++]) { | |
| case SEALED: | |
| assertTrue(subclass.isSealed()); | |
| break; | |
| case FINAL: | |
| assertTrue(Modifier.isFinal(subclass.getModifiers())); | |
| break; | |
| case NON_SEALED: | |
| assertTrue(!subclass.isSealed() && !Modifier.isFinal(subclass.getModifiers())); | |
| break; | |
| } | |
| } | |
| } | |
| } |