OpenModulesTest.java revision 3875:f94e974fe589
1/*
2 * Copyright (c) 2016, 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 tests for multi-module mode compilation
27 * @library /tools/lib
28 * @modules jdk.compiler/com.sun.tools.javac.api
29 *          jdk.compiler/com.sun.tools.javac.main
30 *          jdk.jdeps/com.sun.tools.classfile
31 *          jdk.jdeps/com.sun.tools.javap
32 * @build toolbox.ToolBox toolbox.JavacTask toolbox.ModuleBuilder ModuleTestBase
33 * @run main OpenModulesTest
34 */
35
36import java.io.OutputStream;
37import java.nio.file.Files;
38import java.nio.file.Path;
39import java.util.Arrays;
40import java.util.HashMap;
41import java.util.List;
42import java.util.Map;
43import java.util.Objects;
44
45import com.sun.tools.classfile.Attribute;
46import com.sun.tools.classfile.Attributes;
47import com.sun.tools.classfile.ClassFile;
48import com.sun.tools.classfile.ClassWriter;
49import com.sun.tools.classfile.Module_attribute;
50import toolbox.JavacTask;
51import toolbox.JavapTask;
52import toolbox.Task;
53import toolbox.Task.Expect;
54import toolbox.Task.OutputKind;
55
56public class OpenModulesTest extends ModuleTestBase {
57
58    public static void main(String... args) throws Exception {
59        new OpenModulesTest().runTests();
60    }
61
62    @Test
63    public void testStrongModule(Path base) throws Exception {
64        Path m1 = base.resolve("m1x");
65        tb.writeJavaFiles(m1,
66                          "module m1x { exports api1; opens api2; }",
67                          "package api1; public class Api1 {}",
68                          "package api2; public class Api2 {}",
69                          "package impl; public class Impl {}");
70        Path classes = base.resolve("classes");
71        Path m1Classes = classes.resolve("m1x");
72        tb.createDirectories(m1Classes);
73
74        String log = new JavacTask(tb)
75                .outdir(m1Classes)
76                .files(findJavaFiles(m1))
77                .run()
78                .writeAll()
79                .getOutput(Task.OutputKind.DIRECT);
80
81        if (!log.isEmpty())
82            throw new Exception("expected output not found: " + log);
83
84        String decompiled = new JavapTask(tb)
85                .options("--system", "none", "-bootclasspath", "")
86                .classes(m1Classes.resolve("module-info.class").toString())
87                .run()
88                .writeAll()
89                .getOutput(OutputKind.DIRECT)
90                .replace(System.getProperty("line.separator"), "\n")
91                .replaceAll("@[^;]*;", ";");
92
93        String expected = "module m1x {\n" +
94                          "  requires java.base;\n" +
95                          "  exports api1;\n" +
96                          "  opens api2;\n" +
97                          "}";
98
99        if (!decompiled.contains(expected)) {
100            throw new Exception("expected output not found: " + decompiled);
101        }
102
103        //compiling against a strong module read from binary:
104        Path m2 = base.resolve("m2x");
105        tb.writeJavaFiles(m2,
106                          "module m2x { requires m1x; }",
107                          "package test; public class Test { api1.Api1 a1; api2.Api2 a2; }");
108        Path m2Classes = classes.resolve("m2x");
109        tb.createDirectories(m2Classes);
110
111        List<String> log2 = new JavacTask(tb)
112                .options("--module-path", m1Classes.toString(),
113                         "-XDrawDiagnostics")
114                .outdir(m2Classes)
115                .files(findJavaFiles(m2))
116                .run(Expect.FAIL)
117                .writeAll()
118                .getOutputLines(Task.OutputKind.DIRECT);
119
120        List<String> expected2 = Arrays.asList("Test.java:1:49: compiler.err.package.not.visible: api2, (compiler.misc.not.def.access.not.exported: api2, m1x)",
121                                               "1 error");
122        if (!Objects.equals(log2, expected2))
123            throw new Exception("expected output not found: " + log2);
124    }
125
126    @Test
127    public void testOpenModule(Path base) throws Exception {
128        Path m1 = base.resolve("m1x");
129        tb.writeJavaFiles(m1,
130                          "open module m1x { exports api1; }",
131                          "package api1; public class Api1 {}",
132                          "package api2; public class Api2 {}",
133                          "package impl; public class Impl {}");
134        Path classes = base.resolve("classes");
135        Path m1Classes = classes.resolve("m1x");
136        tb.createDirectories(m1Classes);
137
138        String log = new JavacTask(tb)
139                .outdir(m1Classes)
140                .files(findJavaFiles(m1))
141                .run()
142                .writeAll()
143                .getOutput(Task.OutputKind.DIRECT);
144
145        if (!log.isEmpty())
146            throw new Exception("expected output not found: " + log);
147
148        String decompiled = new JavapTask(tb)
149                .options("--system", "none", "-bootclasspath", "")
150                .classes(m1Classes.resolve("module-info.class").toString())
151                .run()
152                .writeAll()
153                .getOutput(OutputKind.DIRECT)
154                .replace(System.getProperty("line.separator"), "\n")
155                .replaceAll("@[^;]*;", ";");
156
157        String expected = "open module m1x {\n" +
158                          "  requires java.base;\n" +
159                          "  exports api1;\n" +
160                          "}";
161
162        if (!decompiled.contains(expected)) {
163            throw new Exception("expected output not found: " + decompiled);
164        }
165
166        //compiling against a ordinary module read from binary:
167        Path m2 = base.resolve("m2x");
168        tb.writeJavaFiles(m2,
169                          "module m2x { requires m1x; }",
170                          "package test; public class Test { api1.Api1 a1; api2.Api2 a2; }");
171        Path m2Classes = classes.resolve("m2x");
172        tb.createDirectories(m2Classes);
173
174        List<String> log2 = new JavacTask(tb)
175                .options("--module-path", m1Classes.toString(),
176                         "-XDrawDiagnostics")
177                .outdir(m2Classes)
178                .files(findJavaFiles(m2))
179                .run(Expect.FAIL)
180                .writeAll()
181                .getOutputLines(Task.OutputKind.DIRECT);
182
183        List<String> expected2 = Arrays.asList("Test.java:1:49: compiler.err.package.not.visible: api2, (compiler.misc.not.def.access.not.exported: api2, m1x)",
184                                               "1 error");
185        if (!Objects.equals(log2, expected2))
186            throw new Exception("expected output not found: " + log2);
187    }
188
189    @Test
190    public void testOpenModuleNoOpens(Path base) throws Exception {
191        Path m1 = base.resolve("m1x");
192        tb.writeJavaFiles(m1,
193                          "open module m1x { exports api1; opens api2; }",
194                          "package api1; public class Api1 {}",
195                          "package api2; public class Api2 {}",
196                          "package impl; public class Impl {}");
197        Path classes = base.resolve("classes");
198        Path m1Classes = classes.resolve("m1x");
199        tb.createDirectories(m1Classes);
200
201        List<String> log = new JavacTask(tb)
202                .options("-XDrawDiagnostics")
203                .outdir(m1Classes)
204                .files(findJavaFiles(m1))
205                .run(Expect.FAIL)
206                .writeAll()
207                .getOutputLines(Task.OutputKind.DIRECT);
208
209        List<String> expected = Arrays.asList("module-info.java:1:33: compiler.err.no.opens.unless.strong",
210                                              "1 error");
211
212        if (!expected.equals(log))
213            throw new Exception("expected output not found: " + log);
214
215    }
216
217    @Test
218    public void testNonZeroOpensInOpen(Path base) throws Exception {
219        Path m1 = base.resolve("m1x");
220        tb.writeJavaFiles(m1,
221                          "module m1x { opens api; }",
222                          "package api; public class Api {}");
223        Path classes = base.resolve("classes");
224        Path m1Classes = classes.resolve("m1x");
225        tb.createDirectories(m1Classes);
226
227        new JavacTask(tb)
228            .options("-XDrawDiagnostics")
229            .outdir(m1Classes)
230            .files(findJavaFiles(m1))
231            .run(Expect.SUCCESS)
232            .writeAll();
233
234        Path miClass = m1Classes.resolve("module-info.class");
235        ClassFile cf = ClassFile.read(miClass);
236        Module_attribute module = (Module_attribute) cf.attributes.map.get(Attribute.Module);
237        Module_attribute newModule = new Module_attribute(module.attribute_name_index,
238                                                          module.module_name,
239                                                          module.module_flags | Module_attribute.ACC_OPEN,
240                                                          module.module_version_index,
241                                                          module.requires,
242                                                          module.exports,
243                                                          module.opens,
244                                                          module.uses_index,
245                                                          module.provides);
246        Map<String, Attribute> attrs = new HashMap<>(cf.attributes.map);
247
248        attrs.put(Attribute.Module, newModule);
249
250        Attributes newAttributes = new Attributes(attrs);
251        ClassFile newClassFile = new ClassFile(cf.magic,
252                                               cf.minor_version,
253                                               cf.major_version,
254                                               cf.constant_pool,
255                                               cf.access_flags,
256                                               cf.this_class,
257                                               cf.super_class,
258                                               cf.interfaces,
259                                               cf.fields,
260                                               cf.methods,
261                                               newAttributes);
262
263        try (OutputStream out = Files.newOutputStream(miClass)) {
264            new ClassWriter().write(newClassFile, out);
265        }
266
267        Path test = base.resolve("test");
268        tb.writeJavaFiles(test,
269                          "package impl; public class Impl extends api.Api {}");
270        Path testClasses = base.resolve("test-classes");
271        tb.createDirectories(testClasses);
272
273        List<String> log = new JavacTask(tb)
274                .options("-XDrawDiagnostics",
275                         "--module-path", classes.toString(),
276                         "--add-modules", "m1x")
277                .outdir(testClasses)
278                .files(findJavaFiles(test))
279                .run(Expect.FAIL)
280                .writeAll()
281                .getOutputLines(Task.OutputKind.DIRECT);
282
283        List<String> expected = Arrays.asList(
284                "- compiler.err.cant.access: m1x.module-info, "
285                        + "(compiler.misc.bad.class.file.header: module-info.class, "
286                        + "(compiler.misc.module.non.zero.opens: m1x))",
287                                              "1 error");
288
289        if (!expected.equals(log))
290            throw new Exception("expected output not found: " + log);
291    }
292
293}
294