1 /*
   2  * Copyright (c) 2017, 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 import java.io.File;
  25 import java.io.PrintWriter;
  26 import java.io.StringWriter;
  27 import java.nio.file.Files;
  28 import java.nio.file.Path;
  29 import java.nio.file.Paths;
  30 import java.util.ArrayList;
  31 import java.util.List;
  32 import java.util.spi.ToolProvider;
  33 import java.util.stream.Collectors;
  34 import java.util.stream.Stream;
  35 
  36 import static jdk.testlibrary.Asserts.assertTrue;
  37 
  38 import org.testng.annotations.BeforeTest;
  39 import org.testng.annotations.Test;
  40 import static org.testng.Assert.*;
  41 
  42 /**
  43  * @test
  44  * @bug 8174826
  45  * @library /lib/testlibrary
  46  * @modules jdk.charsets jdk.compiler jdk.jlink
  47  * @build SuggestProviders CompilerUtils
  48  * @run testng SuggestProviders
  49  */
  50 
  51 public class SuggestProviders {
  52     private static final String JAVA_HOME = System.getProperty("java.home");
  53     private static final String TEST_SRC = System.getProperty("test.src");
  54 
  55     private static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
  56     private static final Path MODS_DIR = Paths.get("mods");
  57 
  58     private static final String MODULE_PATH =
  59         Paths.get(JAVA_HOME, "jmods").toString() +
  60         File.pathSeparator + MODS_DIR.toString();
  61 
  62     // the names of the modules in this test
  63     private static String[] modules = new String[] {"m1", "m2", "m3", "m4"};
  64 
  65 
  66     private static boolean hasJmods() {
  67         if (!Files.exists(Paths.get(JAVA_HOME, "jmods"))) {
  68             System.err.println("Test skipped. NO jmods directory");
  69             return false;
  70         }
  71         return true;
  72     }
  73 
  74     /*
  75      * Compiles all modules used by the test
  76      */
  77     @BeforeTest
  78     public void compileAll() throws Throwable {
  79         if (!hasJmods()) return;
  80 
  81         for (String mn : modules) {
  82             Path msrc = SRC_DIR.resolve(mn);
  83             assertTrue(CompilerUtils.compile(msrc, MODS_DIR,
  84                 "--module-source-path", SRC_DIR.toString()));
  85         }
  86     }
  87 
  88     @Test
  89     public void suggestProviders() throws Throwable {
  90         if (!hasJmods()) return;
  91 
  92         List<String> output = JLink.run("--module-path", MODULE_PATH,
  93                                         "--add-modules", "m1",
  94                                         "--suggest-providers").output();
  95         // check a subset of services used by java.base
  96         List<String> expected = List.of(
  97             "uses java.lang.System$LoggerFinder",
  98             "uses java.net.ContentHandlerFactory",
  99             "uses java.net.spi.URLStreamHandlerProvider",
 100             "uses java.nio.channels.spi.AsynchronousChannelProvider",
 101             "uses java.nio.channels.spi.SelectorProvider",
 102             "uses java.nio.charset.spi.CharsetProvider",
 103             "uses java.nio.file.spi.FileSystemProvider",
 104             "uses java.nio.file.spi.FileTypeDetector",
 105             "uses java.security.Provider",
 106             "uses java.util.spi.ToolProvider",
 107             "uses p1.S",
 108             "module jdk.charsets provides java.nio.charset.spi.CharsetProvider, used by java.base",
 109             "module jdk.compiler provides java.util.spi.ToolProvider, used by java.base",
 110             "module jdk.jlink provides java.util.spi.ToolProvider, used by java.base",
 111             "module m1 provides p1.S, used by m1",
 112             "module m2 provides p1.S, used by m1"
 113         );
 114 
 115         assertTrue(output.containsAll(expected));
 116     }
 117 
 118     @Test
 119     public void providersForServices() throws Throwable {
 120         if (!hasJmods()) return;
 121 
 122         List<String> output =
 123             JLink.run("--module-path", MODULE_PATH,
 124                       "--add-modules", "m1",
 125                       "--suggest-providers",
 126                       "java.nio.charset.spi.CharsetProvider,p1.S,p2.T").output();
 127 
 128         System.out.println(output);
 129         List<String> expected = List.of(
 130             "module jdk.charsets provides java.nio.charset.spi.CharsetProvider, used by java.base",
 131             "module m1 provides p1.S, used by m1",
 132             "module m2 provides p1.S, used by m1",
 133             "module m2 provides p2.T, used by m2",
 134             "module m3 provides p2.T, used by m2"
 135         );
 136 
 137         assertTrue(output.containsAll(expected));
 138     }
 139 
 140     @Test
 141     public void unusedService() throws Throwable {
 142         if (!hasJmods()) return;
 143 
 144         List<String> output =
 145             JLink.run("--module-path", MODULE_PATH,
 146                 "--add-modules", "m1",
 147                 "--suggest-providers",
 148                 "nonExistentType").output();
 149 
 150         System.out.println(output);
 151         List<String> expected = List.of(
 152             "Services specified in --suggest-providers not used: nonExistentType"
 153         );
 154 
 155         assertTrue(output.containsAll(expected));
 156     }
 157 
 158     @Test
 159     public void noSuggestProviders() throws Throwable {
 160         if (!hasJmods()) return;
 161 
 162         List<String> output =
 163             JLink.run("--module-path", MODULE_PATH,
 164                       "--add-modules", "m1",
 165                       "--bind-services",
 166                       "--limit-modules", "m1,m2,m3,java.base",
 167                       "--suggest-providers").output();
 168 
 169         String expected = "--bind-services option is specified. No additional providers suggested.";
 170         assertTrue(output.contains(expected));
 171 
 172     }
 173 
 174     @Test
 175     public void suggestTypeNotRealProvider() throws Throwable {
 176         if (!hasJmods()) return;
 177 
 178         List<String> output =
 179                 JLink.run("--module-path", MODULE_PATH,
 180                           "--add-modules", "m1",
 181                           "--suggest-providers",
 182                           "java.util.List").output();
 183 
 184         System.out.println(output);
 185         List<String> expected = List.of(
 186                 "Services specified in --suggest-providers not used: java.util.List"
 187         );
 188 
 189         assertTrue(output.containsAll(expected));
 190     }
 191 
 192     @Test
 193     public void noOneUsesProvider() throws Throwable {
 194         if (!hasJmods()) return;
 195 
 196         List<String> output =
 197                 JLink.run("--module-path", MODULE_PATH,
 198                           "--add-modules", "m4",
 199                           "--suggest-providers",
 200                           "p4.S").output();
 201 
 202         System.out.println(output);
 203         List<String> expected = List.of(
 204                 "module m4 provides p4.S, not used by any observable module"
 205         );
 206 
 207         assertTrue(output.containsAll(expected));
 208     }
 209 
 210     @Test
 211     public void nonObservableModule() throws Throwable {
 212         if (!hasJmods()) return;
 213 
 214         List<String> output =
 215                 JLink.runWithNonZeroExitCode(
 216                         "--module-path", MODULE_PATH,
 217                         "--add-modules", "nonExistentModule",
 218                         "--suggest-providers",
 219                         "java.nio.charset.spi.CharsetProvider").output();
 220 
 221         System.out.println(output);
 222         List<String> expected = List.of(
 223                 "Error: Module nonExistentModule not found"
 224         );
 225 
 226         assertTrue(output.containsAll(expected));
 227     }
 228 
 229     static class JLink {
 230         static final ToolProvider JLINK_TOOL = ToolProvider.findFirst("jlink")
 231             .orElseThrow(() ->
 232                 new RuntimeException("jlink tool not found")
 233             );
 234 
 235         static JLink run(String... options) {
 236             JLink jlink = new JLink();
 237             assertTrue(jlink.execute(options) == 0);
 238             return jlink;
 239         }
 240 
 241         static JLink runWithNonZeroExitCode(String... options) {
 242             JLink jlink = new JLink();
 243             assertNotEquals(jlink.execute(options), 0,
 244                     "Exit code should be not 0");
 245             return jlink;
 246         }
 247 
 248         final List<String> output = new ArrayList<>();
 249         private int execute(String... options) {
 250             System.out.println("jlink " +
 251                 Stream.of(options).collect(Collectors.joining(" ")));
 252 
 253             StringWriter writer = new StringWriter();
 254             PrintWriter pw = new PrintWriter(writer);
 255             int rc = JLINK_TOOL.run(pw, pw, options);
 256             System.out.println(writer.toString());
 257             Stream.of(writer.toString().split("\\v"))
 258                   .map(String::trim)
 259                   .forEach(output::add);
 260             return rc;
 261         }
 262 
 263         boolean contains(String s) {
 264             return output.contains(s);
 265         }
 266 
 267         List<String> output() {
 268             return output;
 269         }
 270     }
 271 }