1/*
2 * Copyright (c) 2015, 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 * @bug 8161906 8161596
27 * @summary tests for "requires static"
28 * @library /tools/lib
29 * @modules
30 *      jdk.compiler/com.sun.tools.javac.api
31 *      jdk.compiler/com.sun.tools.javac.main
32 * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase
33 * @run main RequiresStaticTest
34 */
35
36import java.io.File;
37import java.nio.file.Files;
38import java.nio.file.Path;
39
40import toolbox.JavaTask;
41import toolbox.JavacTask;
42import toolbox.Task;
43import toolbox.Task.OutputKind;
44
45public class RequiresStaticTest extends ModuleTestBase {
46
47    public static void main(String... args) throws Exception {
48        RequiresStaticTest t = new RequiresStaticTest();
49        t.runTests();
50    }
51
52    @Test
53    public void testJavaSE_OK(Path base) throws Exception {
54        Path src = base.resolve("src");
55        tb.writeJavaFiles(src,
56                "module m { requires static java.se; }",
57                "import java.awt.Frame;\n"  // in java.se
58                + "class Test {\n"
59                + "    Frame f;\n"
60                + "}");
61        Path classes = base.resolve("classes");
62        Files.createDirectories(classes);
63
64        new JavacTask(tb, Task.Mode.CMDLINE)
65                .files(findJavaFiles(src))
66                .outdir(classes)
67                .run()
68                .writeAll();
69    }
70
71    @Test
72    public void testJavaSE_Fail(Path base) throws Exception {
73        Path src = base.resolve("src");
74        tb.writeJavaFiles(src,
75                "module m { requires static java.se; }",
76                "import com.sun.source.tree.Tree;\n" // not in java.se (in jdk.compiler)
77                + "class Test {\n"
78                + "    Tree t;\n"
79                + "}");
80        Path classes = base.resolve("classes");
81        Files.createDirectories(classes);
82
83        String log = new JavacTask(tb, Task.Mode.CMDLINE)
84                .options("-XDrawDiagnostics")
85                .files(findJavaFiles(src))
86                .outdir(classes)
87                .run(Task.Expect.FAIL)
88                .writeAll()
89                .getOutput(Task.OutputKind.DIRECT);
90
91        if (!log.contains("Test.java:1:22: compiler.err.package.not.visible: com.sun.source.tree, (compiler.misc.not.def.access.does.not.read: m, com.sun.source.tree, jdk.compiler)"))
92            throw new Exception("expected output not found");
93    }
94
95    @Test
96    public void testComplex_OK(Path base) throws Exception {
97        Path src = getComplexSrc(base, "", "");
98        Path classes = base.resolve("classes");
99        Files.createDirectories(classes);
100
101        new JavacTask(tb, Task.Mode.CMDLINE)
102                .options("--module-source-path", src.toString())
103                .files(findJavaFiles(src))
104                .outdir(classes)
105                .run()
106                .writeAll();
107    }
108
109    @Test
110    public void testComplex_Fail(Path base) throws Exception {
111        Path src = getComplexSrc(base,
112                "import p5.C5; import p6.C6; import p7.C7; import p8.C8;\n",
113                "C5 c5; C6 c6; C7 c7; C8 c8;\n");
114        Path classes = base.resolve("classes");
115        Files.createDirectories(classes);
116
117        String log = new JavacTask(tb, Task.Mode.CMDLINE)
118                .options("-XDrawDiagnostics",
119                        "--module-source-path", src.toString())
120                .files(findJavaFiles(src))
121                .outdir(classes)
122                .run(Task.Expect.FAIL)
123                .writeAll()
124                .getOutput(Task.OutputKind.DIRECT);
125
126        String[] expect = {
127            "C1.java:5:8: compiler.err.package.not.visible: p5, (compiler.misc.not.def.access.does.not.read: m1x, p5, m5x)",
128            "C1.java:5:22: compiler.err.package.not.visible: p6, (compiler.misc.not.def.access.does.not.read: m1x, p6, m6x)",
129            "C1.java:5:36: compiler.err.package.not.visible: p7, (compiler.misc.not.def.access.does.not.read: m1x, p7, m7x)",
130            "C1.java:5:50: compiler.err.package.not.visible: p8, (compiler.misc.not.def.access.does.not.read: m1x, p8, m8x)"
131        };
132
133        for (String e: expect) {
134            if (!log.contains(e))
135                throw new Exception("expected output not found: " + e);
136        }
137    }
138
139    /*
140     * Set up the following module graph
141     *     m1x -> m2x => m3x -=-> m4x --> m5
142     *            \           /
143     *              \       /
144     *                v   v
145     *                  m6x => m7x --> m8
146     * where -> is requires, => is requires transitive, --> is requires static, -=-> is requires transitive static
147     */
148    Path getComplexSrc(Path base, String m1_extraImports, String m1_extraUses) throws Exception {
149        Path src = base.resolve("src");
150
151        Path src_m1 = src.resolve("m1x");
152        tb.writeJavaFiles(src_m1,
153                "module m1x { requires m2x; }",
154                "package p1;\n"
155                + "import p2.C2;\n"
156                + "import p3.C3;\n"
157                + "import p4.C4;\n"
158                + m1_extraImports
159                + "class C1 {\n"
160                + "  C2 c2; C3 c3; C4 c4;\n"
161                + m1_extraUses
162                + "}\n");
163
164        Path src_m2 = src.resolve("m2x");
165        tb.writeJavaFiles(src_m2,
166                "module m2x {\n"
167                + "  requires transitive m3x;\n"
168                + "  requires static m6x;\n"
169                + "  exports p2;\n"
170                + "}",
171                "package p2;\n"
172                + "public class C2 {p7.C7 c7; p6.C6 c6; p4.C4 c4;}\n");
173
174        Path src_m3 = src.resolve("m3x");
175        tb.writeJavaFiles(src_m3,
176                "module m3x { requires transitive static m4x; exports p3; }",
177                "package p3;\n"
178                + "public class C3 { }\n");
179
180        Path src_m4 = src.resolve("m4x");
181        tb.writeJavaFiles(src_m4,
182                "module m4x { requires m5x; requires static m6x; exports p4; }",
183                "package p4;\n"
184                + "public class C4 { p6.C6 c6; p7.C7 c7;}\n");
185
186        Path src_m5 = src.resolve("m5x");
187        tb.writeJavaFiles(src_m5,
188                "module m5x { exports p5; }",
189                "package p5;\n"
190                + "public class C5 { }\n");
191
192        Path src_m6 = src.resolve("m6x");
193        tb.writeJavaFiles(src_m6,
194                "module m6x { requires transitive m7x; exports p6; }",
195                "package p6;\n"
196                + "public class C6 { p7.C7 c7; }\n");
197
198        Path src_m7 = src.resolve("m7x");
199        tb.writeJavaFiles(src_m7,
200                "module m7x { requires static m8x; exports p7; }",
201                "package p7;\n"
202                + "public class C7 { p8.C8 c8; }\n");
203
204        Path src_m8 = src.resolve("m8x");
205        tb.writeJavaFiles(src_m8,
206                "module m8x { exports p8; }",
207                "package p8;\n"
208                        + "public class C8 { }\n");
209
210        return src;
211    }
212
213    @Test
214    public void testRequiresStatic(Path base) throws Exception {
215        Path src = base.resolve("src");
216        Path m1 = src.resolve("m1x");
217        tb.writeJavaFiles(m1,
218                "module m1x { exports m1x; }",
219                "package m1x;" +
220                "public class Api { }\n");
221
222        Path classes = base.resolve("classes");
223        Path m1Classes = classes.resolve("m1x");
224        Files.createDirectories(m1Classes);
225
226        new JavacTask(tb, Task.Mode.CMDLINE)
227                .files(findJavaFiles(m1))
228                .outdir(m1Classes)
229                .run()
230                .writeAll();
231
232        Path m3 = src.resolve("m3x");
233        tb.writeJavaFiles(m3,
234                "module m3x { requires static m1x; }",
235                "package m3x;\n" +
236                "public class Test {\n" +
237                "    public static void main(String... args) {\n" +
238                "        try {\n" +
239                "           Class.forName(\"m1x.Api\");\n" +
240                "        } catch (ClassNotFoundException e) {\n" +
241                "            System.err.println(\"ok\");\n" +
242                "        }\n" +
243                "    }\n" +
244                "}",
245                "package m3x;\n" +
246                "public class ApiUse{\n" +
247                "    m1x.Api api;\n" +
248                "}");
249
250        Path m3Classes = classes.resolve("m3x");
251        Files.createDirectories(m3Classes);
252
253        new JavacTask(tb, Task.Mode.CMDLINE)
254                .options("--module-path", m1Classes.toString())
255                .files(findJavaFiles(m3))
256                .outdir(m3Classes)
257                .run()
258                .writeAll();
259
260        String log = new JavaTask(tb)
261                .vmOptions("--module-path", m3Classes.toString(), "--add-modules", "m3x")
262                .className("m3x.Test")
263                .run()
264                .writeAll()
265                .getOutput(OutputKind.STDERR);
266
267        String expected = "ok" + System.getProperty("line.separator");
268
269        if (!expected.equals(log)) {
270            throw new AssertionError("Unexpected output: " + log);
271        }
272    }
273
274    @Test
275    public void testRequiresTransitiveStatic(Path base) throws Exception {
276        Path src = base.resolve("src");
277        Path m1 = src.resolve("m1x");
278        tb.writeJavaFiles(m1,
279                "module m1x { exports m1x; }",
280                "package m1x;" +
281                "public class Api { }\n");
282
283        Path classes = base.resolve("classes");
284        Path m1Classes = classes.resolve("m1x");
285        Files.createDirectories(m1Classes);
286
287        new JavacTask(tb, Task.Mode.CMDLINE)
288                .files(findJavaFiles(m1))
289                .outdir(m1Classes)
290                .run()
291                .writeAll();
292
293        Path m2 = src.resolve("m2x");
294        tb.writeJavaFiles(m2,
295                "module m2x { requires transitive static m1x; }");
296
297        Path m2Classes = classes.resolve("m2x");
298        Files.createDirectories(m2Classes);
299
300        new JavacTask(tb, Task.Mode.CMDLINE)
301                .options("--module-path", m1Classes.toString())
302                .files(findJavaFiles(m2))
303                .outdir(m2Classes)
304                .run()
305                .writeAll();
306
307        Path m3 = src.resolve("m3x");
308        tb.writeJavaFiles(m3,
309                "module m3x { requires m2x; }",
310                "package m3x;\n" +
311                "public class Test {\n" +
312                "    public static void main(String... args) {\n" +
313                "        try {\n" +
314                "           Class.forName(\"m1x.Api\");\n" +
315                "        } catch (ClassNotFoundException e) {\n" +
316                "            System.err.println(\"ok\");\n" +
317                "        }\n" +
318                "    }\n" +
319                "}",
320                "package m3x;\n" +
321                "public class ApiUse{\n" +
322                "    m1x.Api api;\n" +
323                "}");
324
325        Path m3Classes = classes.resolve("m3x");
326        Files.createDirectories(m3Classes);
327
328        new JavacTask(tb, Task.Mode.CMDLINE)
329                .options("--module-path", m1Classes.toString() + File.pathSeparator + m2Classes.toString())
330                .files(findJavaFiles(m3))
331                .outdir(m3Classes)
332                .run()
333                .writeAll();
334
335        String log = new JavaTask(tb)
336                .vmOptions("--module-path", m2Classes.toString() + File.pathSeparator + m3Classes.toString(),
337                           "--add-modules", "m3x")
338                .className("m3x.Test")
339                .run()
340                .writeAll()
341                .getOutput(OutputKind.STDERR);
342
343        String expected = "ok" + System.getProperty("line.separator");
344
345        if (!expected.equals(log)) {
346            throw new AssertionError("Unexpected output: " + log);
347        }
348    }
349
350    @Test
351    public void testRequiresStaticTransitive(Path base) throws Exception {
352        Path src = base.resolve("src");
353        Path m1 = src.resolve("m1x");
354        tb.writeJavaFiles(m1,
355                "module m1x { exports m1x; }",
356                "package m1x;" +
357                "public class Api { }\n");
358
359        Path classes = base.resolve("classes");
360        Path m1Classes = classes.resolve("m1x");
361        Files.createDirectories(m1Classes);
362
363        new JavacTask(tb, Task.Mode.CMDLINE)
364                .files(findJavaFiles(m1))
365                .outdir(m1Classes)
366                .run()
367                .writeAll();
368
369        Path m2 = src.resolve("m2x");
370        tb.writeJavaFiles(m2,
371                "module m2x { requires transitive static m1x; }");
372
373        Path m2Classes = classes.resolve("m2x");
374        Files.createDirectories(m2Classes);
375
376        new JavacTask(tb, Task.Mode.CMDLINE)
377                .options("--module-path", m1Classes.toString())
378                .files(findJavaFiles(m2))
379                .outdir(m2Classes)
380                .run()
381                .writeAll();
382    }
383}
384