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