• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package crossvmtest.java.lang;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotEquals;
22 import static org.junit.Assert.assertThrows;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 
26 import org.junit.Assert;
27 import org.junit.Test;
28 
29 import java.io.ByteArrayInputStream;
30 import java.io.ByteArrayOutputStream;
31 import java.io.IOException;
32 import java.io.NotSerializableException;
33 import java.io.ObjectInputStream;
34 import java.io.ObjectOutputStream;
35 import java.io.Serializable;
36 import java.lang.annotation.ElementType;
37 import java.lang.annotation.Retention;
38 import java.lang.annotation.RetentionPolicy;
39 import java.lang.annotation.Target;
40 import java.lang.invoke.MethodHandles;
41 import java.lang.invoke.VarHandle;
42 import java.lang.reflect.Constructor;
43 import java.lang.reflect.Field;
44 import java.util.Arrays;
45 import java.util.function.IntFunction;
46 import java.util.function.Supplier;
47 
48 public class RecordTest {
49 
RecordInteger(int x)50     record RecordInteger(int x) {}
51 
52     private static class NonRecordInteger {
53 
NonRecordInteger(int x)54         NonRecordInteger(int x) {
55             this.x = x;
56         }
57         private final int x;
58     }
59 
60     @Retention(RetentionPolicy.RUNTIME)
61     @Target({ElementType.FIELD, ElementType.RECORD_COMPONENT})
62     public @interface CustomAnnotation {
value()63         String value();
64     }
65     @Retention(RetentionPolicy.RUNTIME)
66     @Target({ElementType.FIELD, ElementType.RECORD_COMPONENT})
67     public @interface CustomAnnotation2 {
68 
customAnnotations()69         CustomAnnotation[] customAnnotations();
70     }
RecordInteger2(@ustomAnnotation2customAnnotations = {@CustomAnnotation("a")}) @ustomAnnotation"b") int x)71     record RecordInteger2(@CustomAnnotation2(customAnnotations = {@CustomAnnotation("a")})
72                           @CustomAnnotation("b") int x) {}
73 
RecordString(String s)74     record RecordString(String s) {
75         public static final int Y = 1;
76         public static final String A = "A";
77 
78     }
79 
80     public record SerializableRecord(int x, String s) implements Serializable {}
81 
82     @Test
testHashCode()83     public void testHashCode() {
84         RecordInteger a = new RecordInteger(9);
85         RecordInteger b = new RecordInteger(9);
86         RecordInteger c = new RecordInteger(0);
87 
88         assertEquals(a.hashCode(), b.hashCode());
89         assertNotEquals(a.hashCode(), c.hashCode());
90     }
91 
92     @Test
testEquals()93     public void testEquals() {
94         RecordInteger a = new RecordInteger(9);
95         RecordInteger b = new RecordInteger(9);
96         RecordInteger c = new RecordInteger(0);
97 
98         assertTrue(a.equals(b));
99         assertEquals(a, b);
100         assertFalse(a.equals(c));
101         assertNotEquals(a, c);
102     }
103 
104     @Test
testToString()105     public void testToString() {
106         RecordInteger a = new RecordInteger(9);
107         RecordInteger b = new RecordInteger(9);
108         RecordInteger c = new RecordInteger(0);
109 
110         assertEquals(a.toString(), b.toString());
111         assertNotEquals(a.toString(), c.toString());
112     }
113 
114     @Test
testIsRecord()115     public void testIsRecord() throws Exception {
116         assertFalse(Object.class.isRecord());
117         assertFalse(Record.class.isRecord());
118         assertFalse(String.class.isRecord());
119         assertFalse(NonRecordInteger.class.isRecord());
120 
121         RecordInteger a = new RecordInteger(9);
122         assertTrue(a.getClass().isRecord());
123         assertTrue(RecordInteger2.class.isRecord());
124     }
125 
126     @Test
testReflectedConstructor()127     public void testReflectedConstructor() throws ReflectiveOperationException {
128         RecordInteger a = new RecordInteger(9);
129 
130         Constructor<?> c = RecordInteger.class.getDeclaredConstructors()[0];
131         assertEquals(Arrays.deepToString(c.getParameters()), 1, c.getParameters().length);
132         assertEquals(c.getParameters()[0].toString(), "x", c.getParameters()[0].getName());
133         RecordInteger b = (RecordInteger) c.newInstance(9);
134         assertEquals(a.x, b.x);
135         assertEquals(a.x(), b.x());
136         assertEquals(a, b);
137     }
138 
139     @Test
testReadField()140     public void testReadField() throws ReflectiveOperationException {
141         RecordInteger a = new RecordInteger(9);
142         assertEquals(9, a.x);
143         assertEquals(9, a.x());
144 
145         Field[] fields = RecordInteger.class.getDeclaredFields();
146         assertEquals(Arrays.deepToString(fields), 1, fields.length);
147         Field field = fields[0];
148         field.setAccessible(true);
149         assertEquals(field.toString(), "x", field.getName());
150         assertEquals(9, field.get(a));
151     }
152 
153     @Test
testWriteField()154     public void testWriteField() throws ReflectiveOperationException {
155         NonRecordInteger a = new NonRecordInteger(8);
156         Field fieldA = NonRecordInteger.class.getDeclaredField("x");
157         fieldA.setAccessible(true);
158         fieldA.set(a, 7);
159         assertEquals(7, a.x);
160 
161         RecordInteger b = new RecordInteger(8);
162 
163         Field fieldB = RecordInteger.class.getDeclaredField("x");
164         assertThrows(IllegalAccessException.class, () -> fieldB.setInt(b, 7));
165         assertThrows(IllegalAccessException.class, () -> fieldB.set(b, 7));
166         fieldB.setAccessible(true);
167         assertThrows(IllegalAccessException.class, () -> fieldB.setInt(b, 7));
168         assertThrows(IllegalAccessException.class, () -> fieldB.set(b, 7));
169         assertEquals(8, b.x);
170 
171         Field fieldC = RecordString.class.getDeclaredField("s");
172         RecordString c = new RecordString("a");
173         assertThrows(IllegalAccessException.class, () -> fieldC.set(c, "b"));
174         fieldC.setAccessible(true);
175         assertThrows(IllegalAccessException.class, () -> fieldC.set(c, "b"));
176         assertEquals("a", c.s);
177     }
178 
179     @Test
testWriteStaticField()180     public void testWriteStaticField() throws ReflectiveOperationException {
181         Field field = RecordString.class.getField("A");
182         assertThrows(IllegalAccessException.class, () -> field.set(null, "B"));
183         field.setAccessible(true);
184         assertThrows(IllegalAccessException.class, () -> field.set(null, "B"));
185         assertEquals("A", field.get(null));
186 
187         Field fieldB = RecordString.class.getDeclaredField("Y");
188         assertThrows(IllegalAccessException.class, () -> fieldB.setInt(null, 0));
189         fieldB.setAccessible(true);
190         assertThrows(IllegalAccessException.class, () -> fieldB.setInt(null, 0));
191         assertEquals(1, fieldB.getInt(null));
192     }
193 
194     @Test
testVarHandleWrite()195     public void testVarHandleWrite() throws ReflectiveOperationException {
196         NonRecordInteger a = new NonRecordInteger(8);
197 
198         MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(NonRecordInteger.class,
199                 MethodHandles.lookup());
200         VarHandle varHandle = lookup.findVarHandle(NonRecordInteger.class, "x", int.class);
201         assertEquals(8, varHandle.get(a));
202         assertThrows(UnsupportedOperationException.class, () -> varHandle.set(a, 6));
203         assertEquals(8, a.x);
204 
205         RecordInteger b = new RecordInteger(8);
206 
207         lookup = MethodHandles.privateLookupIn(RecordInteger.class, MethodHandles.lookup());
208         VarHandle varHandleB = lookup.findVarHandle(RecordInteger.class, "x", int.class);
209         assertThrows(UnsupportedOperationException.class, () -> varHandleB.set(b, 7));
210         assertEquals(8, b.x);
211     }
212 
213     @Test
testSerializedNonSerializableRecordFailure()214     public void testSerializedNonSerializableRecordFailure()
215             throws IOException, ClassNotFoundException {
216         ByteArrayOutputStream baos = new ByteArrayOutputStream();
217         RecordInteger recordInteger = new RecordInteger(9);
218         try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
219             oos.writeObject(recordInteger);
220             fail("Expect NotSerializableException");
221         } catch (NotSerializableException e) {
222             // expected
223         }
224     }
225 
226     @Test
testSerializedSimpleRecords()227     public void testSerializedSimpleRecords() throws IOException, ClassNotFoundException {
228         ByteArrayOutputStream baos = new ByteArrayOutputStream();
229         SerializableRecord recordInteger = new SerializableRecord(9, "abc");
230         try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
231             oos.writeObject(recordInteger);
232         }
233         byte[] bytes = baos.toByteArray();
234         try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
235             Object obj = ois.readObject();
236             assertEquals(SerializableRecord.class, obj.getClass());
237             SerializableRecord r = (SerializableRecord) obj;
238             assertEquals(9, r.x());
239             assertEquals("abc", r.s());
240         }
241     }
242 
243     @Test
testLocalRecord()244     public void testLocalRecord() {
245         record Point(int x, int y) {
246             @Override
247             public int y() {
248                 return Math.abs(y);
249             }
250 
251             public long sum() {
252                 return (long) x + y;
253             }
254         }
255         var r = new Point(3, 4);
256         assertEquals(3, r.x);
257         assertEquals(4, r.y());
258         assertEquals(7, r.sum());
259         r = new Point(-6, -7);
260         assertEquals(-6, r.x);
261         assertEquals(-6, r.x());
262         assertEquals(-7, r.y);
263         assertEquals(7, r.y());
264         assertEquals(-13, r.sum());
265     }
266 
267     record SupplierRecord(int x)  implements Supplier<String> {
268 
269         private static int A = 9;
270 
setStatic(int a)271         static void setStatic(int a) {
272             A = a;
273         }
274 
getStatic()275         static int getStatic() {
276             return A;
277         }
278 
279         @Override
get()280         public String get() {
281             return String.valueOf(x);
282         }
283     }
284 
285     @Test
testOverriddenInterfaceMethod()286     public void testOverriddenInterfaceMethod() {
287         var r = new SupplierRecord(5);
288         assertEquals(5, r.x);
289         assertEquals("5", r.get());
290     }
291 
292     @Test
testStaticMethods()293     public void testStaticMethods() {
294         SupplierRecord.setStatic(5);
295         assertEquals(5, SupplierRecord.getStatic());
296         SupplierRecord.setStatic(3);
297         assertEquals(3, SupplierRecord.getStatic());
298     }
299 }
300