gt:                  Set<String> unexpectedFilenames = new HashSet<>();
context: /**
   * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  
  /*
   * @test
   * @bug 8142968 8166568 8166286 8170618 8168149
   * @summary Basic test for jmod
   * @library /lib/testlibrary
   * @modules jdk.compiler
   *          jdk.jlink
   * @build jdk.testlibrary.FileUtils CompilerUtils
   * @run testng/othervm -Djava.io.tmpdir=. JmodTest
   */
  
  import java.io.*;
  import java.lang.module.ModuleDescriptor;
  import java.lang.reflect.Method;
  import java.nio.file.*;
  import java.util.*;
  import java.util.function.Consumer;
  import java.util.regex.Pattern;
  import java.util.spi.ToolProvider;
  import java.util.stream.Collectors;
  import java.util.stream.Stream;
  import jdk.testlibrary.FileUtils;
  import org.testng.annotations.BeforeTest;
  import org.testng.annotations.Test;
  
  import static java.io.File.pathSeparator;
  import static java.lang.module.ModuleDescriptor.Version;
  import static java.nio.charset.StandardCharsets.UTF_8;
  import static java.util.stream.Collectors.toSet;
  import static org.testng.Assert.*;
  
  public class JmodTest {
  
      static final ToolProvider JMOD_TOOL = ToolProvider.findFirst("jmod")
          .orElseThrow(() ->
              new RuntimeException("jmod tool not found")
          );
  
      static final String TEST_SRC = System.getProperty("test.src", ".");
      static final Path SRC_DIR = Paths.get(TEST_SRC, "src");
      static final Path EXPLODED_DIR = Paths.get("build");
      static final Path MODS_DIR = Paths.get("jmods");
  
      static final String CLASSES_PREFIX = "classes/";
      static final String CMDS_PREFIX = "bin/";
      static final String LIBS_PREFIX = "lib/";
      static final String CONFIGS_PREFIX = "conf/";
  
      @BeforeTest
      public void buildExplodedModules() throws IOException {
          if (Files.exists(EXPLODED_DIR))
              FileUtils.deleteFileTreeWithRetry(EXPLODED_DIR);
  
          for (String name : new String[] { "foo"/*, "bar", "baz"*/ } ) {
              Path dir = EXPLODED_DIR.resolve(name);
              assertTrue(compileModule(name, dir.resolve("classes")));
              copyResource(SRC_DIR.resolve("foo"),
                           dir.resolve("classes"),
                           "jdk/test/foo/resources/foo.properties");
              createCmds(dir.resolve("bin"));
              createLibs(dir.resolve("lib"));
              createConfigs(dir.resolve("conf"));
          }
  
          if (Files.exists(MODS_DIR))
              FileUtils.deleteFileTreeWithRetry(MODS_DIR);
          Files.createDirectories(MODS_DIR);
      }
  
      // JDK-8166286 - jmod fails on symlink to directory
      @Test
      public void testSymlinks() throws IOException {
          Path apaDir = EXPLODED_DIR.resolve("apa");
          Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes");
          assertTrue(compileModule("apa", classesDir));
          Path libDir = apaDir.resolve("lib");
          createFiles(libDir, List.of("foo/bar/libfoo.so"));
          try {
              Path link = Files.createSymbolicLink(
                  libDir.resolve("baz"), libDir.resolve("foo").toAbsolutePath());
              assertTrue(Files.exists(link));
          } catch (UnsupportedOperationException uoe) {
              // OS does not support symlinks. Nothing to test!
              return;
          }
  
          Path jmod = MODS_DIR.resolve("apa.jmod");
          jmod("create",
               "--libs=", libDir.toString(),
               "--class-path", classesDir.toString(),
               jmod.toString())
              .assertSuccess();
      }
  
      // JDK-8170618 - jmod should validate if any exported or open package is missing
      @Test
      public void testMissingPackages() throws IOException {
          Path apaDir = EXPLODED_DIR.resolve("apa");
          Path classesDir = EXPLODED_DIR.resolve("apa").resolve("classes");
          if (Files.exists(classesDir))
              FileUtils.deleteFileTreeWithRetry(classesDir);
          assertTrue(compileModule("apa", classesDir));
          FileUtils.deleteFileTreeWithRetry(classesDir.resolve("jdk"));
          Path jmod = MODS_DIR.resolve("apa.jmod");
          jmod("create",
               "--class-path", classesDir.toString(),
               jmod.toString())
              .assertFailure()
              .resultChecker(r -> {
                  assertContains(r.output, "Packages that are exported or open in apa are not present: [jdk.test.apa]");
              });
          if (Files.exists(classesDir))
              FileUtils.deleteFileTreeWithRetry(classesDir);
      }
  
      @Test
      public void testList() throws IOException {
          String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
          jmod("create",
               "--class-path", cp,
               MODS_DIR.resolve("foo.jmod").toString())
              .assertSuccess();
  
          jmod("list",
               MODS_DIR.resolve("foo.jmod").toString())
              .assertSuccess()
              .resultChecker(r -> {
                  // asserts dependent on the exact contents of foo
                  assertContains(r.output, CLASSES_PREFIX + "module-info.class");
                  assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/Foo.class");
                  assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/internal/Message.class");
                  assertContains(r.output, CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties");
              });
      }
  
      @Test
      public void testExtractCWD() throws IOException {
          Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
          jmod("create",
               "--class-path", cp.toString(),
               MODS_DIR.resolve("fooExtractCWD.jmod").toString())
              .assertSuccess();
  
          jmod("extract",
               MODS_DIR.resolve("fooExtractCWD.jmod").toString())
              .assertSuccess()
              .resultChecker(r -> {
                  // module-info should exist, but jmod will have added its Packages attr.
                  assertTrue(Files.exists(Paths.get("classes/module-info.class")));
                  assertSameContent(cp.resolve("jdk/test/foo/Foo.class"),
                                    Paths.get("classes/jdk/test/foo/Foo.class"));
                  assertSameContent(cp.resolve("jdk/test/foo/internal/Message.class"),
                                    Paths.get("classes/jdk/test/foo/internal/Message.class"));
                  assertSameContent(cp.resolve("jdk/test/foo/resources/foo.properties"),
                                    Paths.get("classes/jdk/test/foo/resources/foo.properties"));
              });
      }
  
      @Test
      public void testExtractDir() throws IOException {
          if (Files.exists(Paths.get("extractTestDir")))
              FileUtils.deleteFileTreeWithRetry(Paths.get("extractTestDir"));
          Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
          Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");
          Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
          Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");
  
          jmod("create",
               "--conf", cf.toString(),
               "--cmds", bp.toString(),
               "--libs", lp.toString(),
               "--class-path", cp.toString(),
               MODS_DIR.resolve("fooExtractDir.jmod").toString())
              .assertSuccess();
  
          jmod("extract",
               "--dir", "extractTestDir",
               MODS_DIR.resolve("fooExtractDir.jmod").toString())
              .assertSuccess();
  
          jmod("extract",
               "--dir", "extractTestDir",
               MODS_DIR.resolve("fooExtractDir.jmod").toString())
              .assertSuccess()
              .resultChecker(r -> {
                  // check a sample of the extracted files
                  Path p = Paths.get("extractTestDir");
                  assertTrue(Files.exists(p.resolve("classes/module-info.class")));
                  assertSameContent(cp.resolve("jdk/test/foo/Foo.class"),
                                    p.resolve("classes/jdk/test/foo/Foo.class"));
                  assertSameContent(bp.resolve("first"),
                                    p.resolve(CMDS_PREFIX).resolve("first"));
                  assertSameContent(lp.resolve("first.so"),
                                    p.resolve(LIBS_PREFIX).resolve("second.so"));
                  assertSameContent(cf.resolve("second.cfg"),
                                    p.resolve(CONFIGS_PREFIX).resolve("second.cfg"));
              });
      }
  
      @Test
      public void testMainClass() throws IOException {
          Path jmod = MODS_DIR.resolve("fooMainClass.jmod");
          FileUtils.deleteFileIfExistsWithRetry(jmod);
          String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
  
          jmod("create",
               "--class-path", cp,
               "--main-class", "jdk.test.foo.Foo",
               jmod.toString())
              .assertSuccess()
              .resultChecker(r -> {
                  Optional<String> omc = getModuleDescriptor(jmod).mainClass();
                  assertTrue(omc.isPresent());
                  assertEquals(omc.get(), "jdk.test.foo.Foo");
              });
      }
  
      @Test
      public void testModuleVersion() throws IOException {
          Path jmod = MODS_DIR.resolve("fooVersion.jmod");
          FileUtils.deleteFileIfExistsWithRetry(jmod);
          String cp = EXPLODED_DIR.resolve("foo").resolve("classes").toString();
  
          jmod("create",
               "--class-path", cp,
               "--module-version", "5.4.3",
               jmod.toString())
              .assertSuccess()
              .resultChecker(r -> {
                  Optional<Version> ov = getModuleDescriptor(jmod).version();
                  assertTrue(ov.isPresent());
                  assertEquals(ov.get().toString(), "5.4.3");
              });
      }
  
      @Test
      public void testConfig() throws IOException {
          Path jmod = MODS_DIR.resolve("fooConfig.jmod");
          FileUtils.deleteFileIfExistsWithRetry(jmod);
          Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
          Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");
  
          jmod("create",
               "--class-path", cp.toString(),
               "--config", cf.toString(),
               jmod.toString())
              .assertSuccess()
              .resultChecker(r -> {
                  try (Stream<String> s1 = findFiles(cf).map(p -> CONFIGS_PREFIX + p);
                       Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {
                      Set<String> expectedFilenames = Stream.concat(s1, s2)
                                                            .collect(toSet());
                      assertJmodContent(jmod, expectedFilenames);
                  }
              });
      }
  
      @Test
      public void testCmds() throws IOException {
          Path jmod = MODS_DIR.resolve("fooCmds.jmod");
          FileUtils.deleteFileIfExistsWithRetry(jmod);
          Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
          Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");
  
          jmod("create",
               "--cmds", bp.toString(),
               "--class-path", cp.toString(),
               jmod.toString())
              .assertSuccess()
              .resultChecker(r -> {
                  try (Stream<String> s1 = findFiles(bp).map(p -> CMDS_PREFIX + p);
                       Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {
                      Set<String> expectedFilenames = Stream.concat(s1,s2)
                                                            .collect(toSet());
                      assertJmodContent(jmod, expectedFilenames);
                  }
              });
      }
  
      @Test
      public void testLibs() throws IOException {
          Path jmod = MODS_DIR.resolve("fooLibs.jmod");
          FileUtils.deleteFileIfExistsWithRetry(jmod);
          Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
          Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
  
          jmod("create",
               "--libs=", lp.toString(),
               "--class-path", cp.toString(),
               jmod.toString())
              .assertSuccess()
              .resultChecker(r -> {
                  try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);
                       Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p)) {
                      Set<String> expectedFilenames = Stream.concat(s1,s2)
                                                            .collect(toSet());
                      assertJmodContent(jmod, expectedFilenames);
                  }
              });
      }
  
      @Test
      public void testAll() throws IOException {
          Path jmod = MODS_DIR.resolve("fooAll.jmod");
          FileUtils.deleteFileIfExistsWithRetry(jmod);
          Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
          Path bp = EXPLODED_DIR.resolve("foo").resolve("bin");
          Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
          Path cf = EXPLODED_DIR.resolve("foo").resolve("conf");
  
          jmod("create",
               "--conf", cf.toString(),
               "--cmds=", bp.toString(),
               "--libs=", lp.toString(),
               "--class-path", cp.toString(),
               jmod.toString())
              .assertSuccess()
              .resultChecker(r -> {
                  try (Stream<String> s1 = findFiles(lp).map(p -> LIBS_PREFIX + p);
                       Stream<String> s2 = findFiles(cp).map(p -> CLASSES_PREFIX + p);
                       Stream<String> s3 = findFiles(bp).map(p -> CMDS_PREFIX + p);
                       Stream<String> s4 = findFiles(cf).map(p -> CONFIGS_PREFIX + p)) {
                      Set<String> expectedFilenames = Stream.concat(Stream.concat(s1,s2),
                                                                    Stream.concat(s3, s4))
                                                            .collect(toSet());
                      assertJmodContent(jmod, expectedFilenames);
                  }
              });
      }
  
      @Test
      public void testExcludes() throws IOException {
          Path jmod = MODS_DIR.resolve("fooLibs.jmod");
          FileUtils.deleteFileIfExistsWithRetry(jmod);
          Path cp = EXPLODED_DIR.resolve("foo").resolve("classes");
          Path lp = EXPLODED_DIR.resolve("foo").resolve("lib");
  
          jmod("create",
               "--libs=", lp.toString(),
               "--class-path", cp.toString(),
               "--exclude", "**internal**",
               "--exclude", "first.so",
               jmod.toString())
               .assertSuccess()
               .resultChecker(r -> {
                   Set<String> expectedFilenames = new HashSet<>();
                   expectedFilenames.add(CLASSES_PREFIX + "module-info.class");
                   expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/Foo.class");
                   expectedFilenames.add(CLASSES_PREFIX + "jdk/test/foo/resources/foo.properties");
                   expectedFilenames.add(LIBS_PREFIX + "second.so");
                   expectedFilenames.add(LIBS_PREFIX + "third/third.so");
                   assertJmodContent(jmod, expectedFilenames);
  
  