1 /* 2 * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8235369 8235550 8247444 27 * @summary reflection test for records 28 * @compile RecordReflectionTest.java 29 * @run testng/othervm RecordReflectionTest 30 * @run testng/othervm/java.security.policy=allPermissions.policy RecordReflectionTest 31 */ 32 package test.java.lang.reflect.records; 33 34 import java.lang.annotation.*; 35 import java.lang.reflect.*; 36 import java.util.List; 37 import org.testng.annotations.*; 38 import static org.testng.Assert.*; 39 40 @Test 41 public class RecordReflectionTest { 42 43 class NoRecord {} 44 R1()45 record R1() {} 46 R2(int i, int j)47 record R2(int i, int j) {} 48 R3(List<String> ls)49 record R3(List<String> ls) {} 50 R4(R1 r1, R2 r2, R3 r3)51 record R4(R1 r1, R2 r2, R3 r3) {} 52 R5(String... args)53 record R5(String... args) {} 54 55 record R6(long l, String... args) implements java.io.Serializable {} 56 R7(String s1, String s2, String... args)57 record R7(String s1, String s2, String... args) {} 58 59 record R8<A, B>(A a, B b) implements java.io.Serializable { } 60 61 @DataProvider(name = "recordClasses") recordClassData()62 public Object[][] recordClassData() { 63 return List.of(R1.class, 64 R2.class, 65 R3.class, 66 R4.class, 67 R5.class, 68 R6.class, 69 R7.class, 70 R8.class) 71 .stream().map(c -> new Object[] {c}).toArray(Object[][]::new); 72 } 73 74 @Test(dataProvider = "recordClasses") testIsRecord(Class<?> cls)75 public void testIsRecord(Class<?> cls) { 76 String message = cls.toGenericString(); 77 assertTrue(cls.isRecord()); 78 assertTrue(cls.getSuperclass() == java.lang.Record.class); 79 assertTrue(cls.getRecordComponents() != null); 80 assertTrue(message.contains("record"), message); 81 } 82 83 @DataProvider(name = "notRecordClasses") notRecordClasses()84 public Object[][] notRecordClasses() { 85 return List.of(NoRecord.class, 86 NoRecord[].class, 87 Record.class, // java.lang.Record is not itself a record class 88 Record[].class, 89 byte.class, 90 byte[].class, 91 int.class, 92 int[].class, 93 long.class, 94 long[].class) 95 .stream().map(c -> new Object[] {c}).toArray(Object[][]::new); 96 } 97 98 @Test(dataProvider = "notRecordClasses") testNotARecordClass(Class<?> cls)99 public void testNotARecordClass(Class<?> cls) { 100 assertFalse(cls.isRecord()); 101 assertFalse(cls.getSuperclass() == java.lang.Record.class); 102 assertTrue(cls.getRecordComponents() == null); 103 } 104 105 @DataProvider(name = "reflectionData") reflectionData()106 public Object[][] reflectionData() { 107 return new Object[][] { 108 new Object[] { new R1(), 109 0, 110 null, 111 null, 112 null }, 113 new Object[] { new R2(1, 2), 114 2, 115 new Object[]{ 1, 2 }, 116 new String[]{ "i", "j" }, 117 new String[]{ "int", "int"} }, 118 new Object[] { new R3(List.of("1")), 119 1, 120 new Object[]{ List.of("1") }, 121 new String[]{ "ls" }, 122 new String[]{ "java.util.List<java.lang.String>"} }, 123 new Object[] { new R4(new R1(), new R2(6, 7), new R3(List.of("s"))), 124 3, 125 new Object[]{ new R1(), new R2(6, 7), new R3(List.of("s")) }, 126 new String[]{ "r1", "r2", "r3" }, 127 new String[]{ R1.class.toString(), R2.class.toString(), R3.class.toString()} }, 128 }; 129 } 130 131 @Test(dataProvider = "reflectionData") testRecordReflection(Object recordOb, int numberOfComponents, Object[] values, String[] names, String[] signatures)132 public void testRecordReflection(Object recordOb, 133 int numberOfComponents, 134 Object[] values, 135 String[] names, 136 String[] signatures) 137 throws ReflectiveOperationException 138 { 139 Class<?> recordClass = recordOb.getClass(); 140 assertTrue(recordClass.isRecord()); 141 RecordComponent[] recordComponents = recordClass.getRecordComponents(); 142 assertEquals(recordComponents.length, numberOfComponents); 143 int i = 0; 144 for (RecordComponent rc : recordComponents) { 145 assertEquals(rc.getName(), names[i]); 146 assertEquals(rc.getType(), rc.getAccessor().getReturnType()); 147 assertEquals(rc.getAccessor().invoke(recordOb), values[i]); 148 assertEquals(rc.getAccessor().getGenericReturnType().toString(), signatures[i], 149 String.format("signature of method \"%s\" different from expected signature \"%s\"", 150 rc.getAccessor().getGenericReturnType(), signatures[i])); 151 i++; 152 } 153 } 154 155 @Retention(RetentionPolicy.RUNTIME) 156 @Target({ ElementType.RECORD_COMPONENT, ElementType.FIELD }) 157 @interface RCA {} 158 AnnotatedRec(@CA int i)159 record AnnotatedRec(@RCA int i) {} 160 testDeclAnnotationsInRecordComp()161 public void testDeclAnnotationsInRecordComp() throws Throwable { 162 Class<?> recordClass = AnnotatedRec.class; 163 RecordComponent rc = recordClass.getRecordComponents()[0]; 164 Annotation[] annos = rc.getAnnotations(); 165 assertEquals(annos.length, 1); 166 assertEquals(annos[0].toString(), "@test.java.lang.reflect.records.RecordReflectionTest$RCA()"); 167 168 Field f = recordClass.getDeclaredField("i"); 169 assertEquals(f.getAnnotations().length, 1); 170 assertEquals(f.getAnnotations()[0].toString(), annos[0].toString()); 171 } 172 173 // BEGIN Android-removed: ART doesn't support the Annotated Type APIs. 174 /* 175 @Retention(RetentionPolicy.RUNTIME) 176 @Target({ElementType.TYPE_USE}) 177 @interface TYPE_USE {} 178 179 record TypeAnnotatedRec(@TYPE_USE int i) {} 180 181 public void testTypeAnnotationsInRecordComp() throws Throwable { 182 Class<?> recordClass = TypeAnnotatedRec.class; 183 RecordComponent rc = recordClass.getRecordComponents()[0]; 184 AnnotatedType at = rc.getAnnotatedType(); 185 Annotation[] annos = at.getAnnotations(); 186 assertEquals(annos.length, 1); 187 assertEquals(annos[0].toString(), "@RecordReflectionTest$TYPE_USE()"); 188 189 Field f = recordClass.getDeclaredField("i"); 190 assertEquals(f.getAnnotatedType().getAnnotations().length, 1); 191 assertEquals(f.getAnnotatedType().getAnnotations()[0].toString(), annos[0].toString()); 192 } 193 */ 194 // END Android-removed: ART doesn't support the Annotated Type APIs. 195 196 // Android-changed: ART doesn't prevent writes to record field yet. 197 @Test(enabled = false) testReadOnlyFieldInRecord()198 public void testReadOnlyFieldInRecord() throws Throwable { 199 R2 o = new R2(1, 2); 200 Class<?> recordClass = R2.class; 201 String fieldName = "i"; 202 Field f = recordClass.getDeclaredField(fieldName); 203 // Android-changed: libcore doesn't have the trySetAccessible method yet. 204 // assertTrue(f.trySetAccessible()); 205 f.setAccessible(true); 206 assertTrue(f.get(o) != null); 207 try { 208 f.set(o, null); 209 assertTrue(false, "should fail to set " + fieldName); 210 } catch (IllegalAccessException e) { 211 } 212 } 213 214 } 215