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 8156998
  27  * @summary Test --inherit-runtime-environment
  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.JarTask toolbox.JavacTask toolbox.JavaTask ModuleTestBase
  33  * @run main InheritRuntimeEnvironmentTest
  34  */
  35 
  36 import java.io.IOException;
  37 import java.nio.file.Files;
  38 import java.nio.file.Path;
  39 import java.util.Arrays;
  40 import java.util.Collections;
  41 import java.util.List;
  42 import java.util.stream.Collectors;
  43 
  44 import toolbox.ModuleBuilder;
  45 import toolbox.JavaTask;
  46 import toolbox.JavacTask;
  47 import toolbox.Task;
  48 
  49 /**
  50  * Tests that javac picks up runtime options with --inherit-runtime-environment.
  51  * For each option, javac is first run using the option directly, as a control.
  52  * javac is then run again, with the same option(s) being passed to the runtime,
  53  * and --inherit-runtime-environment being used by javac.
  54  * @author jjg
  55  */
  56 public class InheritRuntimeEnvironmentTest extends ModuleTestBase {
  57     public static void main(String... args) throws Exception {
  58         InheritRuntimeEnvironmentTest t = new InheritRuntimeEnvironmentTest();
  59         t.runTests();
  60     }
  61 
  62     /**
  63      * Tests that code being compiled can access JDK-internal API using -add-exports.
  64      * @param base
  65      * @throws Exception
  66      */
  67     @Test
  68     public void testAddExports(Path base) throws Exception {
  69         Path src = base.resolve("src");
  70         tb.writeJavaFiles(src,
  71                 "class C { com.sun.tools.javac.main.Main main; }");
  72 
  73         new TestCase(base)
  74                 .testOpts("--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED")
  75                 .files(findJavaFiles(src))
  76                 .run();
  77     }
  78 
  79     /**
  80      * Tests that code in the unnamed module can access a module on the module path using --add-modules.
  81      */
  82     @Test
  83     public void testAddModules(Path base) throws Exception {
  84         Path modules = base.resolve("modules");
  85         new ModuleBuilder(tb, "m1")
  86                 .exports("pkg1")
  87                 .classes("package pkg1; public class C1 { }")
  88                 .build(modules);
  89 
  90         Path src = base.resolve("src");
  91         tb.writeJavaFiles(src,
  92                 "class C { pkg1.C1 c1; }");
  93 
  94         new TestCase(base)
  95                 .testOpts("--module-path", modules.toString(), "--add-modules", "m1")
  96                 .files(findJavaFiles(src))
  97                 .run();
  98     }
  99 
 100     /**
 101      * Tests that a module on the module path is not visible when --limit-modules is used to
 102      * restrict the set of observable modules.
 103      */
 104     @Test
 105     public void testLimitModules(Path base) throws Exception {
 106         Path modules = base.resolve("modules");
 107         new ModuleBuilder(tb, "m1")
 108                 .exports("pkg1")
 109                 .classes("package pkg1; public class C1 { }")
 110                 .build(modules);
 111 
 112         Path src = base.resolve("src");
 113         new ModuleBuilder(tb, "m2")
 114                 .requires("m1")
 115                 .classes("package pkg2; public class C2 { pkg1.C1 c1; }")
 116                 .write(src);
 117 
 118         // This is the control, to verify that by default, the module being compiled will
 119         // be able to read modules on the module path
 120         new TestCase(base)
 121                 .testOpts("--module-path", modules.toString())
 122                 .otherOpts("--module-source-path", src.toString())
 123                 .files(findJavaFiles(src))
 124                 .run();
 125 
 126         // This is the test, to verify that the module being compiled will not be able to read
 127         // modules on the module path when a --limit-modules is used
 128         new TestCase(base)
 129                 .testOpts("--module-path", modules.toString(), "--limit-modules", "jdk.compiler")
 130                 .otherOpts("-XDrawDiagnostics",
 131                         "--module-source-path", src.toString())
 132                 .files(findJavaFiles(src))
 133                 .expect(Task.Expect.FAIL, "compiler.err.module.not.found")
 134                 .run();
 135     }
 136 
 137     /**
 138      * Tests that a module being compiled can see another module on the module path
 139      * using --module-path.
 140      */
 141     @Test
 142     public void testModulePath(Path base) throws Exception {
 143         Path modules = base.resolve("modules");
 144         new ModuleBuilder(tb, "m1")
 145                 .exports("pkg1")
 146                 .classes("package pkg1; public class C1 { }")
 147                 .build(modules);
 148 
 149         Path src = base.resolve("src");
 150         new ModuleBuilder(tb, "m2")
 151                 .requires("m1")
 152                 .classes("package pkg2; public class C2 { pkg1.C1 c1; }")
 153                 .write(src);
 154 
 155         new TestCase(base)
 156                 .testOpts("--module-path", modules.toString())
 157                 .otherOpts("--module-source-path", src.toString())
 158                 .files(findJavaFiles(src))
 159                 .run();
 160     }
 161 
 162     /**
 163      * Tests that a module being compiled can see classes patches into an existing module
 164      * with --patch-module
 165      */
 166     @Test
 167     public void testPatchModule(Path base) throws Exception {
 168         Path patchSrc = base.resolve("patchSrc");
 169         tb.writeJavaFiles(patchSrc,
 170                 "package java.util; public class Xyzzy { }");
 171         Path patch = base.resolve("patch");
 172         Files.createDirectories(patch);
 173 
 174         new JavacTask(tb)
 175                 .options("-Xmodule:java.base")
 176                 .outdir(patch)
 177                 .sourcepath(patchSrc)
 178                 .files(findJavaFiles(patchSrc))
 179                 .run()
 180                 .writeAll();
 181 
 182         Path src = base.resolve("src");
 183         tb.writeJavaFiles(src,
 184                 "public class C { java.util.Xyzzy x; }");
 185 
 186         new TestCase(base)
 187                 .testOpts("--patch-module", "java.base=" + patch)
 188                 .files(findJavaFiles(src))
 189                 .run();
 190     }
 191 
 192     /**
 193      * Tests that options in @files are also effective.
 194      * The test is similar to testModulePath, except that the test options are provided in an @-file.
 195      */
 196     @Test
 197     public void testAtFile(Path base) throws Exception {
 198         Path modules = base.resolve("modules");
 199         new ModuleBuilder(tb, "m1")
 200                 .exports("pkg1")
 201                 .classes("package pkg1; public class C1 { }")
 202                 .build(modules);
 203 
 204         Path src = base.resolve("src");
 205         new ModuleBuilder(tb, "m2")
 206                 .requires("m1")
 207                 .classes("package pkg2; public class C2 { pkg1.C1 c1; }")
 208                 .write(src);
 209 
 210         Path atFile = base.resolve("atFile");
 211         tb.writeFile(atFile, "--module-path " + modules);
 212 
 213         new TestCase(base)
 214                 .testOpts("@" + atFile)
 215                 .otherOpts("--module-source-path", src.toString())
 216                 .files(findJavaFiles(src))
 217                 .run();
 218     }
 219 
 220     /**
 221      * Tests that --inherit-runtime-environment works in conjunction with
 222      * environment variables.
 223      * This is a variant of testAddExports.
 224      * The use of environment variables is sufficiently custom that it is
 225      * not easy to do this directly with a simple TestCase.
 226      */
 227     @Test
 228     public void testEnvVars(Path base) throws Exception {
 229         Path src = base.resolve("src");
 230         tb.writeJavaFiles(src,
 231                 "class C { com.sun.tools.javac.main.Main main; }");
 232         List<String> testOpts =
 233                 Arrays.asList("--add-exports", "jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED");
 234         List<Path> files = Arrays.asList(findJavaFiles(src));
 235 
 236         String envName = "_JAVAC_OPTIONS";
 237         String envValue = String.join(" ", testOpts);
 238 
 239         out.println("  javac:");
 240         Path javacOutDir = base.resolve("out-javac");
 241         Files.createDirectories(javacOutDir);
 242 
 243         out.println("    env: " + envName + "=" + envValue);
 244         out.println("    outdir: " + javacOutDir);
 245         out.println("    files: " + files);
 246 
 247         new JavacTask(tb, Task.Mode.EXEC)
 248                 .envVar(envName, envValue)
 249                 .outdir(javacOutDir)
 250                 .files(files)
 251                 .run()
 252                 .writeAll()
 253                 .getOutput(Task.OutputKind.DIRECT);
 254 
 255         out.println("  java:");
 256         Path javaOutDir = base.resolve("out-java");
 257         Files.createDirectories(javaOutDir);
 258 
 259         Path atFile = base.resolve("atFile");
 260         tb.writeFile(atFile, String.join(" ", testOpts));
 261 
 262         List<String> vmOpts = Arrays.asList(
 263                 "@" + atFile,
 264                 "--module", "jdk.compiler/com.sun.tools.javac.Main"
 265         );
 266 
 267         List<String> classArgs = join(
 268                 Arrays.asList("-d", javaOutDir.toString()),
 269                 files.stream()
 270                         .map(p -> p.toString())
 271                         .collect(Collectors.toList())
 272         );
 273 
 274         envValue = "--inherit-runtime-environment";
 275 
 276         out.println("    env: " + envName + "=" + envValue);
 277         out.println("    vmOpts: " + vmOpts);
 278         out.println("    classArgs: " + classArgs);
 279 
 280         new JavaTask(tb)
 281                 .envVar(envName, envValue)
 282                 .vmOptions(vmOpts)
 283                 .classArgs(classArgs)
 284                 .run()
 285                 .writeAll()
 286                 .getOutput(Task.OutputKind.STDERR);
 287     }
 288 
 289     /**
 290      * Runs javac with given test options,  first directly, and then again, specifying the
 291      * options to the runtime, and using --inherit-runtime-environment.
 292      */
 293     class TestCase {
 294         final Path base;
 295         List<String> testOpts = Collections.emptyList();
 296         List<String> otherOpts = Collections.emptyList();
 297         List<Path> files = Collections.emptyList();
 298         Task.Expect expect = Task.Expect.SUCCESS;
 299         String expectedText;
 300 
 301         /**
 302          * Creates a test case, specifying a base directory for work files.
 303          */
 304         TestCase(Path base) {
 305             this.base = base;
 306         }
 307 
 308         /**
 309          * Set the "test options" to be passed to javac or to the runtime.
 310          */
 311         TestCase testOpts(String... testOpts) {
 312             this.testOpts = Arrays.asList(testOpts);
 313             return this;
 314         }
 315 
 316         /**
 317          * Sets additional options required for the compilation.
 318          */
 319         TestCase otherOpts(String... otherOpts) {
 320             this.otherOpts = Arrays.asList(otherOpts);
 321             return this;
 322         }
 323 
 324         /**
 325          * Sets the files to be compiled.
 326          */
 327         TestCase files(Path... files) {
 328             this.files = Arrays.asList(files);
 329             return this;
 330         }
 331 
 332         /**
 333          * Sets the expected output, and any expected output from javac.
 334          * The default is {@code Expect.SUCCESS} and no specific output expected.
 335          */
 336         TestCase expect(Task.Expect expect, String expectedText) {
 337             this.expect = expect;
 338             this.expectedText = expectedText;
 339             return this;
 340         }
 341 
 342         /**
 343          * Runs the test case.
 344          * First, javac is run passing the test options directly to javac.
 345          * Then, javac is run again, passing the test options to the runtime,
 346          * and using --inherit-runtime-environment.
 347          */
 348         void run() throws IOException {
 349             runJavac();
 350             runJava();
 351         }
 352 
 353         private void runJavac() throws IOException {
 354             out.println("  javac:");
 355             Path javacOutDir = base.resolve("out-javac");
 356             Files.createDirectories(javacOutDir);
 357 
 358             List<String> options = join(testOpts, otherOpts);
 359 
 360             out.println("    options: " + options);
 361             out.println("    outdir: " + javacOutDir);
 362             out.println("    files: " + files);
 363 
 364             String log = new JavacTask(tb, Task.Mode.CMDLINE)
 365                     .options(options)
 366                     .outdir(javacOutDir)
 367                     .files(files)
 368                     .run(expect)
 369                     .writeAll()
 370                     .getOutput(Task.OutputKind.DIRECT);
 371 
 372             if (expectedText != null && !log.contains(expectedText))
 373                 error("expected text not found");
 374         }
 375 
 376         private void runJava() throws IOException {
 377             out.println("  java:");
 378             Path javaOutDir = base.resolve("out-java");
 379             Files.createDirectories(javaOutDir);
 380 
 381             List<String> vmOpts = join(
 382                     testOpts,
 383                     Arrays.asList("--module", "jdk.compiler/com.sun.tools.javac.Main")
 384             );
 385 
 386             List<String> classArgs = join(
 387                     Arrays.asList("--inherit-runtime-environment",
 388                             "-d", javaOutDir.toString()),
 389                     otherOpts,
 390                     files.stream()
 391                             .map(p -> p.toString())
 392                             .collect(Collectors.toList())
 393             );
 394 
 395             out.println("    vmOpts: " + vmOpts);
 396             out.println("    classArgs: " + classArgs);
 397 
 398             String log = new JavaTask(tb)
 399                     .vmOptions(vmOpts)
 400                     .classArgs(classArgs)
 401                     .run(expect)
 402                     .writeAll()
 403                     .getOutput(Task.OutputKind.STDERR);
 404 
 405             if (expectedText != null && !log.contains(expectedText))
 406                 error("expected text not found");
 407         }
 408     }
 409 
 410     /**
 411      * Join a series of lists.
 412      */
 413     @SafeVarargs
 414     private <T> List<T> join(List<T>... lists) {
 415         return Arrays.stream(lists)
 416             .flatMap(list -> list.stream())
 417             .collect(Collectors.toList());
 418     }
 419 
 420 }
 421