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  * @bug 8146486
  27  * @summary Fail to create a MR modular JAR with a versioned entry in
  28  *          base-versioned empty package
  29  * @library /lib/testlibrary
  30  * @build jdk.testlibrary.FileUtils
  31  * @run testng ConcealedPackage
  32  */
  33 
  34 import org.testng.Assert;
  35 import org.testng.annotations.AfterClass;
  36 import org.testng.annotations.Test;
  37 
  38 import java.io.ByteArrayOutputStream;
  39 import java.io.IOException;
  40 import java.io.PrintStream;
  41 import java.io.UncheckedIOException;
  42 import java.nio.file.Files;
  43 import java.nio.file.Path;
  44 import java.nio.file.Paths;
  45 import java.util.Arrays;
  46 import java.util.Set;
  47 import java.util.spi.ToolProvider;
  48 import java.util.stream.Collectors;
  49 import java.util.stream.Stream;
  50 
  51 import jdk.testlibrary.FileUtils;
  52 
  53 public class ConcealedPackage {
  54     private static final ToolProvider JAR_TOOL = ToolProvider.findFirst("jar")
  55            .orElseThrow(() -> new RuntimeException("jar tool not found"));
  56     private static final ToolProvider JAVAC_TOOL = ToolProvider.findFirst("javac")
  57             .orElseThrow(() -> new RuntimeException("javac tool not found"));
  58     final private Path userdir;
  59     final private ByteArrayOutputStream outbytes = new ByteArrayOutputStream();
  60     final private PrintStream out = new PrintStream(outbytes, true);
  61     final private ByteArrayOutputStream errbytes = new ByteArrayOutputStream();
  62     final private PrintStream err = new PrintStream(errbytes, true);
  63 
  64     public ConcealedPackage() throws IOException {
  65         Path testsrc = Paths.get(System.getProperty("test.src"));
  66         userdir = Paths.get(System.getProperty("user.dir", "."));
  67 
  68         // compile the classes directory
  69         Path source = testsrc.resolve("src").resolve("classes");
  70         Path destination = Paths.get("classes");
  71         javac(source, destination);
  72 
  73         // compile the mr9 directory including module-info.java
  74         source = testsrc.resolve("src").resolve("mr9");
  75         destination = Paths.get("mr9");
  76         javac(source, destination);
  77 
  78         // move module-info.class for later use
  79         Files.move(destination.resolve("module-info.class"),
  80                 Paths.get("module-info.class"));
  81     }
  82 
  83     private void javac(Path source, Path destination) throws IOException {
  84         String[] args = Stream.concat(
  85                 Stream.of("-d", destination.toString()),
  86                 Files.walk(source)
  87                         .map(Path::toString)
  88                         .filter(s -> s.endsWith(".java"))
  89         ).toArray(String[]::new);
  90         JAVAC_TOOL.run(System.out, System.err, args);
  91     }
  92 
  93     private int jar(String cmd) {
  94         outbytes.reset();
  95         errbytes.reset();
  96         return JAR_TOOL.run(out, err, cmd.split(" +"));
  97     }
  98 
  99     @AfterClass
 100     public void cleanup() throws IOException {
 101         Files.walk(userdir, 1)
 102                 .filter(p -> !p.equals(userdir))
 103                 .forEach(p -> {
 104                     try {
 105                         if (Files.isDirectory(p)) {
 106                             FileUtils.deleteFileTreeWithRetry(p);
 107                         } else {
 108                             FileUtils.deleteFileIfExistsWithRetry(p);
 109                         }
 110                     } catch (IOException x) {
 111                         throw new UncheckedIOException(x);
 112                     }
 113                 });
 114     }
 115 
 116 
 117     @Test // updates a valid multi-release jar with a new public class in
 118           // versioned section and fails
 119     public void test1() {
 120         // successful build of multi-release jar
 121         int rc = jar("-cf mmr.jar -C classes . --release 9 -C mr9 p/Hi.class");
 122         Assert.assertEquals(rc, 0);
 123 
 124         jar("-tf mmr.jar");
 125 
 126         String s = new String(outbytes.toByteArray());
 127         Set<String> actual = Arrays.stream(s.split("\n")).collect(Collectors.toSet());
 128         Set<String> expected = Set.of(
 129                 "META-INF/",
 130                 "META-INF/MANIFEST.MF",
 131                 "p/",
 132                 "p/Hi.class",
 133                 "META-INF/versions/9/p/Hi.class"
 134         );
 135         Assert.assertEquals(actual, expected);
 136 
 137         // failed build because of new public class
 138         rc = jar("-uf mmr.jar --release 9 -C mr9 p/internal/Bar.class");
 139         Assert.assertEquals(rc, 1);
 140 
 141         s = new String(errbytes.toByteArray());
 142         Assert.assertTrue(s.contains("p/internal/Bar.class, contains a new public "
 143                 + "class not found in base entries")
 144         );
 145     }
 146 
 147     @Test // updates a valid multi-release jar with a module-info class and new
 148           // concealed public class in versioned section and succeeds
 149     public void test2() {
 150         // successful build of multi-release jar
 151         int rc = jar("-cf mmr.jar -C classes . --release 9 -C mr9 p/Hi.class");
 152         Assert.assertEquals(rc, 0);
 153 
 154         // successful build because of module-info and new public class
 155         rc = jar("-uf mmr.jar module-info.class --release 9 -C mr9 p/internal/Bar.class");
 156         Assert.assertEquals(rc, 0);
 157 
 158         String s = new String(errbytes.toByteArray());
 159         Assert.assertTrue(s.contains("p/internal/Bar.class is a public class in a "
 160                         + "concealed package, \nplacing this jar on the class path "
 161                         + "will result in incompatible public interfaces")
 162         );
 163 
 164         jar("-tf mmr.jar");
 165 
 166         s = new String(outbytes.toByteArray());
 167         Set<String> actual = Arrays.stream(s.split("\n")).collect(Collectors.toSet());
 168         Set<String> expected = Set.of(
 169                 "META-INF/",
 170                 "META-INF/MANIFEST.MF",
 171                 "p/",
 172                 "p/Hi.class",
 173                 "META-INF/versions/9/p/Hi.class",
 174                 "META-INF/versions/9/p/internal/Bar.class",
 175                 "module-info.class"
 176         );
 177         Assert.assertEquals(actual, expected);
 178     }
 179 
 180     @Test  // jar tool fails building mmr.jar because of new public class
 181     public void test3() {
 182         int rc = jar("-cf mmr.jar -C classes . --release 9 -C mr9 .");
 183         Assert.assertEquals(rc, 1);
 184 
 185         String s = new String(errbytes.toByteArray());
 186         Assert.assertTrue(s.contains("p/internal/Bar.class, contains a new public "
 187                 + "class not found in base entries")
 188         );
 189     }
 190 
 191     @Test  // jar tool succeeds building mmr.jar because of concealed package
 192     public void test4() {
 193         int rc = jar("-cf mmr.jar module-info.class -C classes . " +
 194                 "--release 9 module-info.class -C mr9 .");
 195         Assert.assertEquals(rc, 0);
 196 
 197         String s = new String(errbytes.toByteArray());
 198         Assert.assertTrue(s.contains("p/internal/Bar.class is a public class in a "
 199                 + "concealed package, \nplacing this jar on the class path "
 200                 + "will result in incompatible public interfaces")
 201         );
 202 
 203         jar("-tf mmr.jar");
 204 
 205         s = new String(outbytes.toByteArray());
 206         Set<String> actual = Arrays.stream(s.split("\n")).collect(Collectors.toSet());
 207         Set<String> expected = Set.of(
 208                 "META-INF/",
 209                 "META-INF/MANIFEST.MF",
 210                 "module-info.class",
 211                 "META-INF/versions/9/module-info.class",
 212                 "p/",
 213                 "p/Hi.class",
 214                 "META-INF/versions/9/p/",
 215                 "META-INF/versions/9/p/Hi.class",
 216                 "META-INF/versions/9/p/internal/",
 217                 "META-INF/versions/9/p/internal/Bar.class"
 218         );
 219         Assert.assertEquals(actual, expected);
 220     }
 221 }