1 /*
2  * Copyright (c) 2014, 2018, 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  * @library /test/lib
27  * @modules jdk.compiler
28  *          jdk.jartool
29  *          jdk.jlink
30  * @build BasicTest jdk.test.lib.compiler.CompilerUtils
31  * @run testng BasicTest
32  * @bug 8234076
33  * @summary Basic test of starting an application as a module
34  */
35 
36 import java.io.File;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.nio.file.Paths;
40 import java.util.spi.ToolProvider;
41 
42 import jdk.test.lib.compiler.CompilerUtils;
43 import jdk.test.lib.process.ProcessTools;
44 import jdk.test.lib.process.OutputAnalyzer;
45 import jdk.test.lib.Utils;
46 
47 import org.testng.annotations.BeforeTest;
48 import org.testng.annotations.Test;
49 import static org.testng.Assert.*;
50 
51 
52 @Test
53 public class BasicTest {
54     private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
55         .orElseThrow(() ->
56             new RuntimeException("jar tool not found")
57         );
58     private static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
59         .orElseThrow(() ->
60             new RuntimeException("jmod tool not found")
61         );
62 
63     private static final Path USER_DIR = Paths.get(System.getProperty("user.dir"));
64 
65     private static final String TEST_SRC = System.getProperty("test.src");
66 
67     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
68     private static final Path MODS_DIR = Paths.get("mods");
69 
70     // the module name of the test module
71     private static final String TEST_MODULE = "test";
72 
73     // the module main class
74     private static final String MAIN_CLASS = "jdk.test.Main";
75 
76     // for Windows specific launcher tests
77     static final boolean IS_WINDOWS = System.getProperty("os.name", "unknown").startsWith("Windows");
78 
79     @BeforeTest
compileTestModule()80     public void compileTestModule() throws Exception {
81 
82         // javac -d mods/$TESTMODULE src/$TESTMODULE/**
83         boolean compiled
84             = CompilerUtils.compile(SRC_DIR.resolve(TEST_MODULE),
85                                     MODS_DIR.resolve(TEST_MODULE));
86 
87         assertTrue(compiled, "test module did not compile");
88     }
89 
90     /**
91      * Execute "java" with the given arguments, returning the exit code.
92      */
exec(String... args)93     private int exec(String... args) throws Exception {
94        return ProcessTools.executeTestJava(args)
95                 .outputTo(System.out)
96                 .errorTo(System.out)
97                 .getExitValue();
98     }
99 
100 
101     /**
102      * The initial module is loaded from an exploded module
103      */
testRunWithExplodedModule()104     public void testRunWithExplodedModule() throws Exception {
105         String dir = MODS_DIR.toString();
106         String subdir = MODS_DIR.resolve(TEST_MODULE).toString();
107         String mid = TEST_MODULE + "/" + MAIN_CLASS;
108 
109         // java --module-path mods -module $TESTMODULE/$MAINCLASS
110         int exitValue = exec("--module-path", dir, "--module", mid);
111         assertTrue(exitValue == 0);
112 
113         // java --module-path mods/$TESTMODULE --module $TESTMODULE/$MAINCLASS
114         exitValue = exec("--module-path", subdir, "--module", mid);
115         assertTrue(exitValue == 0);
116 
117         // java --module-path=mods --module=$TESTMODULE/$MAINCLASS
118         exitValue = exec("--module-path=" + dir, "--module=" + mid);
119         assertTrue(exitValue == 0);
120 
121         // java --module-path=mods/$TESTMODULE --module=$TESTMODULE/$MAINCLASS
122         exitValue = exec("--module-path=" + subdir, "--module=" + mid);
123         assertTrue(exitValue == 0);
124 
125         // java -p mods -m $TESTMODULE/$MAINCLASS
126         exitValue = exec("-p", dir, "-m", mid);
127         assertTrue(exitValue == 0);
128 
129         // java -p mods/$TESTMODULE -m $TESTMODULE/$MAINCLASS
130         exitValue = exec("-p", subdir, "-m", mid);
131         assertTrue(exitValue == 0);
132     }
133 
134 
135     /**
136      * The initial module is loaded from a modular JAR file
137      */
testRunWithModularJar()138     public void testRunWithModularJar() throws Exception {
139         Path dir = Files.createTempDirectory(USER_DIR, "mlib");
140         Path jar = dir.resolve("m.jar");
141 
142         // jar --create ...
143         String classes = MODS_DIR.resolve(TEST_MODULE).toString();
144         String[] args = {
145             "--create",
146             "--file=" + jar,
147             "--main-class=" + MAIN_CLASS,
148             "-C", classes, "."
149         };
150         int rc = JAR_TOOL.run(System.out, System.out, args);
151         assertTrue(rc == 0);
152 
153         // java --module-path mlib -module $TESTMODULE
154         int exitValue = exec("--module-path", dir.toString(),
155                              "--module", TEST_MODULE);
156         assertTrue(exitValue == 0);
157 
158         // java --module-path mlib/m.jar -module $TESTMODULE
159         exitValue = exec("--module-path", jar.toString(),
160                          "--module", TEST_MODULE);
161         assertTrue(exitValue == 0);
162     }
163 
164 
165     /**
166      * Attempt to run with the initial module packaged as a JMOD file.
167      */
testTryRunWithJMod()168     public void testTryRunWithJMod() throws Exception {
169         Path dir = Files.createTempDirectory(USER_DIR, "mlib");
170 
171         // jmod create ...
172         String cp = MODS_DIR.resolve(TEST_MODULE).toString();
173         String jmod = dir.resolve("m.jmod").toString();
174         String[] args = {
175             "create",
176             "--class-path", cp,
177             "--main-class", MAIN_CLASS,
178             jmod
179         };
180 
181         assertEquals(JMOD_TOOL.run(System.out, System.out, args), 0);
182 
183         // java --module-path mods --module $TESTMODULE
184         int exitValue = exec("--module-path", dir.toString(),
185                              "--module", TEST_MODULE);
186         assertTrue(exitValue != 0);
187     }
188 
189 
190     /**
191      * Run the test with a non-existent file on the application module path.
192      * It should be silently ignored.
193      */
testRunWithNonExistentEntry()194     public void testRunWithNonExistentEntry() throws Exception {
195         String mp = "DoesNotExist" + File.pathSeparator + MODS_DIR.toString();
196         String mid = TEST_MODULE + "/" + MAIN_CLASS;
197 
198         // java --module-path mods --module $TESTMODULE/$MAINCLASS
199         int exitValue = exec("--module-path", mp, "--module", mid);
200         assertTrue(exitValue == 0);
201     }
202 
203 
204     /**
205      * Attempt to run an unknown initial module
206      */
testTryRunWithBadModule()207     public void testTryRunWithBadModule() throws Exception {
208         String modulepath = MODS_DIR.toString();
209 
210         // java --module-path mods -m $TESTMODULE
211         int exitValue = exec("--module-path", modulepath, "-m", "rhubarb");
212         assertTrue(exitValue != 0);
213     }
214 
215 
216     /**
217      * Attempt to run with -m specifying a main class that does not
218      * exist.
219      */
testTryRunWithBadMainClass()220     public void testTryRunWithBadMainClass() throws Exception {
221         String modulepath = MODS_DIR.toString();
222         String mid = TEST_MODULE + "/p.rhubarb";
223 
224         // java --module-path mods -m $TESTMODULE/$MAINCLASS
225         int exitValue = exec("--module-path", modulepath, "-m", mid);
226         assertTrue(exitValue != 0);
227     }
228 
229 
230     /**
231      * Attempt to run with -m specifying a modular JAR that does not have
232      * a MainClass attribute
233      */
testTryRunWithMissingMainClass()234     public void testTryRunWithMissingMainClass() throws Exception {
235         Path dir = Files.createTempDirectory(USER_DIR, "mlib");
236 
237         // jar --create ...
238         String classes = MODS_DIR.resolve(TEST_MODULE).toString();
239         String jar = dir.resolve("m.jar").toString();
240         String[] args = {
241             "--create",
242             "--file=" + jar,
243             "-C", classes, "."
244         };
245         int rc = JAR_TOOL.run(System.out, System.out, args);
246         assertTrue(rc == 0);
247 
248         // java --module-path mods -m $TESTMODULE
249         int exitValue = exec("--module-path", dir.toString(), "-m", TEST_MODULE);
250         assertTrue(exitValue != 0);
251     }
252 
253 
254     /**
255      * Attempt to run with -m specifying a main class that is a different
256      * module to that specified to -m
257      */
testTryRunWithMainClassInWrongModule()258     public void testTryRunWithMainClassInWrongModule() throws Exception {
259         String modulepath = MODS_DIR.toString();
260         String mid = "java.base/" + MAIN_CLASS;
261 
262         // java --module-path mods --module $TESTMODULE/$MAINCLASS
263         int exitValue = exec("--module-path", modulepath, "--module", mid);
264         assertTrue(exitValue != 0);
265     }
266 
267 
268     /**
269      * Helper method that creates a ProcessBuilder with command line arguments
270      * while setting the _JAVA_LAUNCHER_DEBUG environment variable.
271      */
createProcessWithLauncherDebugging(String... cmds)272     private ProcessBuilder createProcessWithLauncherDebugging(String... cmds) {
273         ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(Utils.addTestJavaOpts(cmds));
274         pb.environment().put("_JAVA_LAUNCHER_DEBUG", "true");
275 
276         return pb;
277     }
278 
279      /**
280      * Test the ability for the Windows launcher to do proper application argument
281      * detection and expansion, when using the long form module option and all passed in
282      * command line arguments are prefixed with a dash.
283      *
284      * These tests are not expected to work on *nixes, and are ignored.
285      */
testWindowsWithLongFormModuleOption()286     public void testWindowsWithLongFormModuleOption() throws Exception {
287         if (!IS_WINDOWS) {
288             return;
289         }
290 
291         String dir = MODS_DIR.toString();
292         String mid = TEST_MODULE + "/" + MAIN_CLASS;
293 
294         // java --module-path=mods --module=$TESTMODULE/$MAINCLASS --help
295         // We should be able to find the argument --help as an application argument
296         ProcessTools.executeProcess(
297             createProcessWithLauncherDebugging(
298                 "--module-path=" + dir,
299                 "--module=" + mid,
300                 "--help"))
301             .outputTo(System.out)
302             .errorTo(System.out)
303             .shouldContain("F--help");
304 
305         // java --module-path=mods --module=$TESTMODULE/$MAINCLASS <...src/test>/*.java --help
306         // We should be able to see argument expansion happen
307         ProcessTools.executeProcess(
308             createProcessWithLauncherDebugging(
309                 "--module-path=" + dir,
310                 "--module=" + mid,
311                 SRC_DIR.resolve(TEST_MODULE).toString() + "\\*.java",
312                 "--help"))
313             .outputTo(System.out)
314             .errorTo(System.out)
315             .shouldContain("F--help")
316             .shouldContain("module-info.java");
317     }
318 
319 
320     /**
321      * Test that --module= is terminating for VM argument processing just like --module
322      */
testLongFormModuleOptionTermination()323     public void testLongFormModuleOptionTermination() throws Exception {
324         String dir = MODS_DIR.toString();
325         String mid = TEST_MODULE + "/" + MAIN_CLASS;
326 
327         // java --module-path=mods --module=$TESTMODULE/$MAINCLASS --module-path=mods --module=$TESTMODULE/$MAINCLASS
328         // The first --module= will terminate the VM arguments processing. The second pair of module-path and module will be
329         // deemed as application arguments
330         OutputAnalyzer output = ProcessTools.executeProcess(
331             createProcessWithLauncherDebugging(
332                 "--module-path=" + dir,
333                 "--module=" + mid,
334                 "--module-path=" + dir,
335                 "--module=" + mid))
336             .outputTo(System.out)
337             .errorTo(System.out)
338             .shouldContain("argv[ 0] = '--module-path=" + dir)
339             .shouldContain("argv[ 1] = '--module=" + mid);
340 
341         if (IS_WINDOWS) {
342             output.shouldContain("F--module-path=" + dir).shouldContain("F--module=" + mid);
343         }
344 
345         // java --module=$TESTMODULE/$MAINCLASS --module-path=mods
346         // This command line will not work as --module= is terminating and the module will be not found
347         int exitValue = exec("--module=" + mid, "--module-path" + dir);
348         assertTrue(exitValue != 0);
349     }
350 }
351