Skip to content

Commit 688b10b

Browse files
author
Vicente Romero
committed
8255561: add tests to check binary compatibility rules for records
Reviewed-by: jjg
1 parent 727a69f commit 688b10b

File tree

1 file changed

+307
-0
lines changed

1 file changed

+307
-0
lines changed
Lines changed: 307 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,307 @@
1+
/*
2+
* Copyright (c) 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+
* @summary test binary compatibility rules for record classes
27+
* @library /tools/lib
28+
* @modules jdk.compiler/com.sun.tools.javac.api
29+
* jdk.compiler/com.sun.tools.javac.main
30+
* jdk.compiler/com.sun.tools.javac.util
31+
* jdk.compiler/com.sun.tools.javac.code
32+
* jdk.jdeps/com.sun.tools.classfile
33+
* @build toolbox.ToolBox toolbox.JavacTask
34+
* @run main RecordsBinaryCompatibilityTests
35+
*/
36+
37+
import java.util.*;
38+
39+
import java.io.IOException;
40+
import java.nio.file.Files;
41+
import java.nio.file.Path;
42+
import java.nio.file.Paths;
43+
import java.util.stream.IntStream;
44+
45+
import com.sun.tools.classfile.*;
46+
import com.sun.tools.javac.code.Flags;
47+
import com.sun.tools.javac.util.Assert;
48+
import toolbox.TestRunner;
49+
import toolbox.ToolBox;
50+
import toolbox.JavaTask;
51+
import toolbox.JavacTask;
52+
import toolbox.Task;
53+
import toolbox.Task.OutputKind;
54+
55+
public class RecordsBinaryCompatibilityTests extends TestRunner {
56+
ToolBox tb;
57+
58+
RecordsBinaryCompatibilityTests() {
59+
super(System.err);
60+
tb = new ToolBox();
61+
}
62+
63+
protected void runTests() throws Exception {
64+
runTests(m -> new Object[]{Paths.get(m.getName())});
65+
}
66+
67+
public static void main(String... args) throws Exception {
68+
RecordsBinaryCompatibilityTests t = new RecordsBinaryCompatibilityTests();
69+
t.runTests();
70+
}
71+
72+
Path[] findJavaFiles(Path... paths) throws IOException {
73+
return tb.findJavaFiles(paths);
74+
}
75+
76+
@Test
77+
public void testCompatibilityAfterAddingRecordComponent(Path base) throws Exception {
78+
testCompatibilityAfterModifyingRecord(
79+
base,
80+
"""
81+
package pkg;
82+
public record R(String i) {}
83+
""",
84+
"""
85+
package pkg;
86+
public record R(String i, String j) {}
87+
""",
88+
"""
89+
package pkg;
90+
public class Client {
91+
public static void main(String... args) {
92+
R r = new R("Hello World!");
93+
System.out.println(r.i());
94+
}
95+
}
96+
""",
97+
true,
98+
NoSuchMethodError.class
99+
);
100+
}
101+
102+
@Test
103+
public void testCompatibilityAfterDeletingRecordComponent(Path base) throws Exception {
104+
testCompatibilityAfterModifyingRecord(
105+
base,
106+
"""
107+
package pkg;
108+
public record R(String i, String j) {
109+
public R(String j) {
110+
this("Hello World!", j);
111+
}
112+
}
113+
""",
114+
"""
115+
package pkg;
116+
public record R(String j) {}
117+
""",
118+
"""
119+
package pkg;
120+
public class Client {
121+
public static void main(String... args) {
122+
R r = new R("Hi");
123+
System.out.println(r.i());
124+
}
125+
}
126+
""",
127+
true,
128+
NoSuchMethodError.class
129+
);
130+
}
131+
132+
@Test
133+
public void testCompatibilityAfterChangingRecordComponent(Path base) throws Exception {
134+
testCompatibilityAfterModifyingRecord(
135+
base,
136+
"""
137+
package pkg;
138+
public record R(String i, double j) {}
139+
""",
140+
"""
141+
package pkg;
142+
public record R(String i, int j) {}
143+
""",
144+
"""
145+
package pkg;
146+
public class Client {
147+
public static void main(String... args) {
148+
R r = new R("Hello World!", 1);
149+
System.out.println(r.i());
150+
}
151+
}
152+
""",
153+
true,
154+
NoSuchMethodError.class
155+
);
156+
}
157+
158+
@Test
159+
public void testCompatibilityAfterReorderingRecordComponents(Path base) throws Exception {
160+
testCompatibilityAfterModifyingRecord(
161+
base,
162+
"""
163+
package pkg;
164+
public record R(String i, int j) {}
165+
""",
166+
"""
167+
package pkg;
168+
public record R(int j, String i) {}
169+
""",
170+
"""
171+
package pkg;
172+
public class Client {
173+
public static void main(String... args) {
174+
R r = new R("Hello World!", 1);
175+
System.out.println(r.i());
176+
}
177+
}
178+
""",
179+
true,
180+
NoSuchMethodError.class
181+
);
182+
}
183+
184+
@Test
185+
public void testCompatibilityAfterChangingRecordComponent2(Path base) throws Exception {
186+
testCompatibilityAfterModifyingRecord(
187+
base,
188+
"""
189+
package pkg;
190+
public record R(String j) {
191+
public static String i() { return "Hello World!"; }
192+
}
193+
""",
194+
"""
195+
package pkg;
196+
public record R(String i) {}
197+
""",
198+
"""
199+
package pkg;
200+
public class Client {
201+
public static void main(String... args) {
202+
R r = new R("Hello World!");
203+
System.out.println(r.i());
204+
}
205+
}
206+
""",
207+
true,
208+
IncompatibleClassChangeError.class
209+
);
210+
}
211+
212+
@Test
213+
public void testCompatibilityAfterChangingRecordComponent3(Path base) throws Exception {
214+
testCompatibilityAfterModifyingRecord(
215+
base,
216+
"""
217+
package pkg;
218+
public record R(String i) {
219+
}
220+
""",
221+
"""
222+
package pkg;
223+
public record R(String j) {}
224+
""",
225+
"""
226+
package pkg;
227+
public class Client {
228+
public static void main(String... args) {
229+
R r = new R("Hello World!");
230+
System.out.println(r.i());
231+
}
232+
}
233+
""",
234+
true,
235+
NoSuchMethodError.class
236+
);
237+
}
238+
239+
/* 1- compiles the first version of the record class source code along with the client source code
240+
* 2- executes the client class just to make sure that it works
241+
* 3- compiles the second version of the record class
242+
* 4- executes the client class and makes sure that the VM throws the expected error or not
243+
* depending on the shouldFail argument
244+
*/
245+
private void testCompatibilityAfterModifyingRecord(
246+
Path base,
247+
String recordCode1,
248+
String recordCode2,
249+
String clientCode,
250+
boolean shouldFail,
251+
Class<?> expectedError) throws Exception {
252+
Path src = base.resolve("src");
253+
Path pkg = src.resolve("pkg");
254+
Path recordSrc = pkg.resolve("R");
255+
Path client = pkg.resolve("Client");
256+
257+
tb.writeJavaFiles(recordSrc, recordCode1);
258+
tb.writeJavaFiles(client, clientCode);
259+
260+
Path out = base.resolve("out");
261+
Files.createDirectories(out);
262+
263+
new JavacTask(tb)
264+
.outdir(out)
265+
.files(findJavaFiles(pkg))
266+
.run();
267+
268+
// let's execute to check that it's working
269+
String output = new JavaTask(tb)
270+
.classpath(out.toString())
271+
.classArgs("pkg.Client")
272+
.run()
273+
.writeAll()
274+
.getOutput(Task.OutputKind.STDOUT);
275+
276+
// let's first check that it runs wo issues
277+
if (!output.contains("Hello World!")) {
278+
throw new AssertionError("execution of Client didn't finish");
279+
}
280+
281+
// now lets change the record class
282+
tb.writeJavaFiles(recordSrc, recordCode2);
283+
284+
new JavacTask(tb)
285+
.outdir(out)
286+
.files(findJavaFiles(recordSrc))
287+
.run();
288+
289+
if (shouldFail) {
290+
// let's now check that we get the expected error
291+
output = new JavaTask(tb)
292+
.classpath(out.toString())
293+
.classArgs("pkg.Client")
294+
.run(Task.Expect.FAIL)
295+
.writeAll()
296+
.getOutput(Task.OutputKind.STDERR);
297+
if (!output.startsWith("Exception in thread \"main\" " + expectedError.getName())) {
298+
throw new AssertionError(expectedError.getName() + " expected");
299+
}
300+
} else {
301+
new JavaTask(tb)
302+
.classpath(out.toString())
303+
.classArgs("pkg.Client")
304+
.run(Task.Expect.SUCCESS);
305+
}
306+
}
307+
}

0 commit comments

Comments
 (0)