--- old/src/share/classes/sun/module/repository/RepositoryConfig.java Wed Aug 6 09:30:39 2008 +++ new/src/share/classes/sun/module/repository/RepositoryConfig.java Wed Aug 6 09:30:39 2008 @@ -34,7 +34,6 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.module.Modules; -import java.module.ModuleSystemPermission; import java.module.Repository; import java.net.MalformedURLException; import java.net.URI; @@ -142,20 +141,10 @@ /** * Sets the application repository. * @param r {@code Repository} that will be the application repository - * SecurityException if a security manager exists and its - * checkPermission method denies access to shutdown the - * repository. * @throws IllegalArgumentException if the application repository has already * been set via this method. - * @throws SecurityException if a security manager exists and its - * {@code checkPermission} method denies access to set the system - * repository. */ public static void setApplicationRepository(Repository r) throws IllegalArgumentException { - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission(new ModuleSystemPermission("setApplicationRepository")); - } if (applicationRepositoryWasSet) { throw new IllegalArgumentException("Application repository is already set."); } else { --- old/src/share/classes/sun/module/tools/JRepo.java Wed Aug 6 09:30:41 2008 +++ new/src/share/classes/sun/module/tools/JRepo.java Wed Aug 6 09:30:41 2008 @@ -45,7 +45,7 @@ import java.text.DateFormat; import java.util.Arrays; import java.util.ArrayList; -import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; @@ -53,6 +53,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import sun.module.repository.RepositoryConfig; import sun.security.action.GetPropertyAction; import sun.tools.jar.CommandLine; @@ -114,8 +115,8 @@ /** Indicates dependencies command should display info on core modules. */ private static final Flag javaseFlag = new Flag('j'); - /** Provides way to specify platform binding fo dependencies command. */ - private static final BindingFlag bindingFlag = new BindingFlag(); + /** Causes an individual command to print help and exit. */ + private static final Flag helpFlag = new Flag('h'); /** Contains the flags that are common to all commands. */ private static final HashMap commonFlags = new HashMap(); @@ -123,6 +124,7 @@ static { repositoryFlag.register(commonFlags); verboseFlag.register(commonFlags); + helpFlag.register(commonFlags); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); @@ -144,10 +146,12 @@ msg = new Messenger("jrepo", out, err, reader); synchronized(commands) { if (commands.isEmpty()) { - new ListCommand().register(commands); + new DependenciesCommand().register(commands); new InstallCommand().register(commands); + new HelpCommand().register(commands); + new ListCommand().register(commands); new UninstallCommand().register(commands); - new DependenciesCommand().register(commands); + new ValidateCommand().register(commands); } } reset(); @@ -170,6 +174,11 @@ return false; } + if (helpFlag.isEnabled()) { + cmd.usageError(msg); + return true; + } + Repository repo = null; try { repo = getRepository(); @@ -225,6 +234,7 @@ parentFlag.reset(); repositoryFlag.reset(); verboseFlag.reset(); + helpFlag.reset(); for (Command cmd : commands.values()) { cmd.reset(); } @@ -264,12 +274,16 @@ try { int numFlags = cmd.parseFlags(args, commonFlags); + if (helpFlag.isEnabled()) { + return cmd; + } + args = Arrays.copyOfRange(args, numFlags + 1, args.length); try { if (cmd.parseArgs(args, msg)) { return cmd; } else { - usageError(); + cmd.usageError(msg); return null; } } catch (ArrayIndexOutOfBoundsException e) { @@ -277,7 +291,7 @@ } } catch (IllegalArgumentException ex) { msg.error(ex.getMessage()); - usageError(); + cmd.usageError(msg); return null; } return cmd; @@ -287,17 +301,6 @@ /** Returns a user-grokkable description of the repository. */ private static String getRepositoryText(Repository repo) { String rc; -/* URI u = repo.getSourceLocation(); - if (u == null) { - rc = "Bootstrap repository"; - } else { - try { - rc = "Repository " + u.toURL().toExternalForm(); - } catch (MalformedURLException ex) { - rc = "Repository unknown"; - } - } - */ rc = "[" + repo.toString() + "]"; return rc; @@ -333,9 +336,6 @@ pw.printf(MDFormat, md.getName(), md.getVersion()); if (verboseFlag.isEnabled()) { pw.printf(MDFormatVerbose, -// md.getRepository() == Repository.getBootstrapRepository() -// ? "bootstrap" -// : md.getRepository().getSourceLocation().toString()); md.getRepository().getName()); } return sw.toString(); @@ -432,43 +432,6 @@ } } - private static class BindingFlag extends Flag { - String platform; - String arch; - - BindingFlag() { - super('b'); - } - - @Override - int set(String[] args, int pos) throws IllegalArgumentException { - int rc = super.set(args, pos); - String[] binding = args[pos + 1].split("-"); - if (binding.length != 2) { - throw new IllegalArgumentException( - "Must 2 and only 2 elements for platform-arch"); - } - platform = binding[0]; - arch = binding[1]; - return rc + 1; - } - - String getPlatform() { - return platform; - } - - String getArch() { - return arch; - } - - @Override - void reset() { - super.reset(); - platform = null; - arch = null; - } - } - /* * Command types: An abstract base class, plus one concrete class for * each Command. @@ -504,7 +467,7 @@ * @return number of flags found. * @throws IllegalArgumentException if an invalid flag is given. */ - int parseFlags(String[] args, Map flags) { +/* int parseFlags(String[] args, Map flags) { int rc = 0; int i = 0; while (i < args.length) { @@ -519,6 +482,9 @@ throw new IllegalArgumentException("unrecognized flag: " // XXX i18n + args[i]); } + } if (s.charAt(0) == '-') { + throw new IllegalArgumentException("invalid flag: " // XXX i18n + + args[i]); } else { i++; } @@ -525,11 +491,44 @@ } return rc; } +*/ + int parseFlags(String[] args, Map flags) { + int rc = 0; + int i = 0; + while (i < args.length) { + String s = args[i]; + if (s.charAt(0) == '-') { + if (s.length() == 2) { + Flag f = flags.get(s.charAt(1)); + if (f != null) { + int numConsumed = f.set(args, i); + i += numConsumed; // Increases at each iteration. + rc += numConsumed; // Increases only when the arg is a flag. + } else { + throw new IllegalArgumentException("unrecognized flag: " // XXX i18n + + args[i]); + } + } else { + throw new IllegalArgumentException("invalid flag: " // XXX i18n + + args[i]); + } + } else { + i++; + } + } + return rc; + } /** Represents the actual behavior of the command. */ abstract boolean run(Repository repo, Messenger msg); - /** Returns a usage string describing this command, or null if the + /** Prints command-specific usage message. */ + void usageError(Messenger msg) { + msg.error("Synopsis for " + name + ": \n" + usage()); + } + + /** + *Returns a usage string describing this command, or null if the * command is a synonym for another command. */ abstract String usage(); @@ -582,13 +581,11 @@ DependenciesCommand() { super("dependencies"); javaseFlag.register(myFlags); - bindingFlag.register(myFlags); } @Override void reset() { javaseFlag.reset(); - bindingFlag.reset(); } @Override @@ -611,9 +608,7 @@ return false; } - if (DEBUG) debug("name: " + name + " ver: " + version - + " plat: " + bindingFlag.getPlatform() - + " arch: " + bindingFlag.getArch()); + if (DEBUG) debug("name: " + name + " ver: " + version); return true; } @@ -624,8 +619,7 @@ ? VersionConstraint.DEFAULT : version.toVersionConstraint()); printHeader(name, vc); - rc = depend(repo, name, vc, - bindingFlag.getPlatform(), bindingFlag.getArch()); + rc = depend(repo, name, vc); printTrailer(null); } else { for (ModuleArchiveInfo mai : repo.list()) { @@ -633,8 +627,7 @@ String maiName = mai.getName(); VersionConstraint vc = mai.getVersion().toVersionConstraint(); printHeader(maiName, vc); - rc &= depend(repo, maiName, vc, - mai.getPlatform(), mai.getArch()); + rc &= depend(repo, maiName, vc); printTrailer("\n"); } } @@ -641,12 +634,11 @@ return rc; } - boolean depend(Repository repo, String name, VersionConstraint constraint, - String platform, String arch) { + boolean depend(Repository repo, String name, VersionConstraint constraint) { ImportTraverser traverser = new ImportTraverser(); ImportTraverser.Visitor visitor = new ImportVisitor(traverser, msg); try { - traverser.traverse(visitor, repo, name, constraint, platform, arch); + traverser.traverse(visitor, repo, name, constraint); if (traverser.traversedAny()) { return true; } @@ -677,8 +669,8 @@ } String usage() { - return "dependencies [-v] [-r repositoryLocation] [-b platform-arch]"// XXX i18n - + " [moduleName [moduleVersion] ]\n" + // XXX i18n + return "dependencies [-v] [-r repositoryLocation] [moduleName [moduleVersion] ]\n" + " Lists all modules on which identified modules depend.\n" + " If no moduleName is given, lists dependencies of all" + " module archives in the repository\n" @@ -735,15 +727,56 @@ msg.println(indent + getMText(m)); } } + + + /** Prints help. */ + private class HelpCommand extends Command { + HelpCommand() { + super("help"); + } + + boolean parseArgs(String[] args, Messenger msg) { + return true; + } + + boolean run(Repository repo, Messenger msg) { + JRepo.this.usageError(msg); + return true; + } + + String usage() { + return "help\n Prints brief help text for all commands.\n" + + " Each command allows a -h option which gives help for that command."; + } + } + /** Installs a JAM into a repository. */ private class InstallCommand extends Command { + @SuppressWarnings("unchecked") + private final Map myFlags = (Map) commonFlags.clone(); + + /** When used, prevents checking of installed module's dependencies. */ + private Flag quickInstallFlag = new Flag('q'); + private String jamName; InstallCommand() { super("install"); + quickInstallFlag.register(myFlags); } + @Override + void reset() { + jamName = null; + quickInstallFlag.reset(); + } + + @Override + int parseFlags(String[] args, Map flags) { + return super.parseFlags(args, myFlags); + } + boolean parseArgs(String[] args, Messenger msg) { boolean rc = false; if (!parentFlag.isEnabled() @@ -780,7 +813,27 @@ if (verboseFlag.isEnabled()) { msg.println("Installed " + jamName + ": " + getMAIText(mai)); } + if (!quickInstallFlag.isEnabled()) { + ModuleDefinition md = repo.find( + mai.getName(), mai.getVersion().toVersionConstraint()); + if (md == null) { + msg.error("Warning: " + jamName + + " was installed but cannot be found"); + } else { + try { + Module m = md.getModuleInstance(); + } catch (ModuleInitializationException ex) { + msg.error("Cannot install " + ex.getMessage()); + if (!repo.uninstall(mai)) { + msg.error("Could not uninstall " + mai); + } + return false; + } + } + } return true; + } catch (IllegalStateException ex) { + msg.error("Cannot install " + jamName + ": " + ex.getMessage()); } catch (URISyntaxException ex) { msg.error("Cannot install " + jamName + ": no such file, or malformed URI"); } catch (MalformedURLException ex) { @@ -793,8 +846,8 @@ } String usage() { - return "install [-v] -r repositoryLocation jamFile | jamURL\n" // XXX i18n - + " installs a module into a repository"; + return "install [-v] [-q] -r repositoryLocation jamFile | jamURL\n" // XXX i18n + + " Installs a module into a repository."; } } @@ -916,7 +969,7 @@ String usage() { return "uninstall [-v] [-f | -i] -r repositoryLocation moduleName"// XXX i18n + " [moduleVersion] [modulePlatformBinding]\n" - + " removes a module from a repository, along with associated files."; + + " Removes a module from a repository, along with associated files."; } /** Uninstall the ModuleArchiveInfo from the Repository. */ @@ -1027,6 +1080,60 @@ } } + /** Provides a way to sort ModuleArchiveInfo instances. */ + static class MAIComparator implements Comparator { + private static final MAIComparator instance = new MAIComparator(); + + private MAIComparator() { } + + static MAIComparator getInstance() { + return instance; + } + + public int compare(ModuleArchiveInfo o1, ModuleArchiveInfo o2) { + int rc = o1.getName().compareTo(o2.getName()); + if (rc != 0) { + return rc; + } + + rc = o1.getVersion().compareTo(o2.getVersion()); + if (rc != 0) { + return rc; + } + + rc = compareStrings(o1.getPlatform(), o2.getPlatform()); + if (rc != 0) { + return rc; + } + + return compareStrings(o1.getArch(), o2.getArch()); + } + + // Compare two Strings. If neither is null, compare them + // lexicographically. If both are null, return 0. If the first is + // null, return -1; else 1. + private int compareStrings(String s1, String s2) { + int rc = 0; + if (s2 != null && s2 != null) { + rc = s1.compareTo(s2); + if (rc != 0) { + return rc; + } + } + if (s1 == null) { + return -1; + } else if (s2 == null) { + return 1; + } + return 0; + } + + @Override + public boolean equals(Object o) { + return this == o; + } + } + /** Prints information about modules found in repositories. */ private class ListCommand extends Command { @SuppressWarnings("unchecked") @@ -1075,8 +1182,9 @@ } String usage() { - return "list [-v] [-p] [-r repositoryLocation] moduleName\n"// XXX i18n - + " lists the modules in the repository"; + return "list [-v] [-p] [-r repositoryLocation] [moduleName]\n"// XXX i18n + + " Lists the modules in the repository that match the given name.\n" + + " If no moduleName is given, lists all modules in the repository."; } class ListRepositoryVisitor extends RepositoryVisitor { @@ -1091,7 +1199,10 @@ msg.println(getRepositoryText(repo)); msg.println(" empty"); } else { - for (ModuleArchiveInfo mai : repo.list()) { + TreeSet sorted = + new TreeSet(MAIComparator.getInstance()); + sorted.addAll(repo.list()); + for (ModuleArchiveInfo mai : sorted) { if (moduleName == null || mai.getName().startsWith(moduleName)) { if (!printedHeader) { msg.println(getRepositoryText(repo)); @@ -1106,4 +1217,115 @@ } } } + + /** Runs shallow and/or deep validation on one or more modules. */ + class ValidateCommand extends Command { + @SuppressWarnings("unchecked") + private final Map myFlags = (Map) commonFlags.clone(); + + /** Indicates that deep validation should be run. */ + private Flag deepValidateFlag = new Flag('d'); + + private String name; + private Version version; + + ValidateCommand() { + super("validate"); + deepValidateFlag.register(myFlags); + } + + @Override + void reset() { + deepValidateFlag.reset(); + } + + @Override + int parseFlags(String[] args, Map flags) { + return super.parseFlags(args, myFlags); + } + + boolean parseArgs(String[] args, Messenger msg) { + if (args.length > 0) { + name = args[0]; + } + if (args.length > 1) { + try { + version = Version.valueOf(args[1]); + } catch (IllegalArgumentException ex) { + return false; + } + } + if (args.length > 2) { + return false; + } + + if (DEBUG) debug("name: " + name + " ver: " + version); + return true; + } + + boolean run(Repository repo, Messenger msg) { + boolean rc = true; + if (name != null) { + VersionConstraint vc = (version == null + ? VersionConstraint.DEFAULT + : version.toVersionConstraint()); + + List mdList = repo.find(Query.module(name, vc)); + if (mdList.size() == 1) { + rc = validate(mdList.get(0), msg); + } else { + throw new IllegalArgumentException( + "more than one matching module found for name=" + name + + " version=" + vc); + } + } else { + for (ModuleDefinition md : repo.findAll()) { + rc &= validate(md, msg); + } + } + return rc; + } + + /** @return true if the module passes validation validation. */ + boolean validate(ModuleDefinition md, Messenger msg) { + boolean rc = false; + try { + Module m = md.getModuleInstance(); + if (deepValidateFlag.isEnabled()) { + if (m.supportsDeepValidation()) { + try { + m.deepValidate(); + if (verboseFlag.isEnabled()) { + msg.println( + "Module " + m + " passed deep validation"); // XXX i18n + } + rc = true; + } catch (ModuleInitializationException ex) { + msg.error( + "Module " + m + " failed deep validation"); // XXX i18n + } + } + } else { + // Module was instantiated => passed shallow validation + if (verboseFlag.isEnabled()) { + msg.println( + "Module " + m + " passed shallow validation"); // XXX i18n + } + rc = true; + } + } catch (ModuleInitializationException ex) { + msg.error("Could not initialize module for " + md // XXX i18n + + ": " + ex.getMessage()); + } + return rc; + } + + String usage() { + // XXX i18n + return "validate [-v] [-d] [-r repositoryLocation] [moduleName [moduleVersion] ]\n" + + " Runs deep validation on the identified modules.\n" + + " If no moduleName is given, validates all module" + + " archives in the repository.\n"; + } + } } --- old/test/java/module/modinit/JRepoDependenciesTest.java Wed Aug 6 09:30:42 2008 +++ new/test/java/module/modinit/JRepoDependenciesTest.java Wed Aug 6 09:30:42 2008 @@ -28,7 +28,6 @@ /** * @test - * @library ../tools ../tools/jrepo * @compile -XDignore.symbol.file * RunMTest.java * classp/MainX.java @@ -131,7 +130,7 @@ } protected void runTest(RunMTest mTest) throws Exception { - Repository parent = sun.module.repository.RepositoryConfig.getSystemRepository(); + Repository parent = sun.module.repository.RepositoryConfig.getApplicationRepository(); Repository repository = Modules.newLocalRepository( mTest.getName(), mTest.outputDirectory, null, parent); --- /dev/null Wed Aug 6 09:30:38 2008 +++ new/test/java/module/modinit/JRepoInstallTest.java Wed Aug 6 09:30:43 2008 @@ -0,0 +1,187 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.io.*; +import java.module.*; +import java.util.*; +import sun.module.JamUtils; +import sun.module.tools.JRepo; + +/** + * @test + * @compile -XDignore.symbol.file + * RunMTest.java + * classp/MainX.java + * JRepoInstallTest.java + * @run main/othervm + * -DTestDescriptionFactory.classname=JRepoInstallTest$MyFactory + * JRepoInstallTest + */ +public class JRepoInstallTest { + private static final boolean debug = Boolean.getBoolean("module.tools.debug"); + + private static final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + private static final PrintStream resultStream = new PrintStream(baos); + + private static final String[] tests = { + "basic/import.mtest", + }; + + + public static void realMain(String args[]) throws Throwable { + RunMTest.main(tests); + } + + /** Return an array of Strings from the given String. */ + static String[] getArgs(String s) { + List args = new ArrayList(); + StringTokenizer st = new StringTokenizer(s); + while (st.hasMoreTokens()) { + String token = st.nextToken(); + if (debug) System.err.println("adding arg " + token); + args.add(token); + } + if (debug) System.err.println("args length is " + args.size()); + return args.toArray(new String[0]); + } + + public static class MyFactory extends RunMTest.TestDescriptionFactory { + + protected RunMTest.TestDescription doCreate(String name) { + return new MyTestDescription(name); + } + } + + public static class MyTestDescription extends RunMTest.TestDescription { + MyTestDescription(String name) { + super(name); + } + + protected void runTest(RunMTest mTest) throws Exception { + if (name.equals("m2")) { + // Only run this for m1. + return; + } + + // Enables shadow file copies in the repository if we're running + // on Windows. This is to prevent file locking in the source + // location. + if (System.getProperty("os.platform").equalsIgnoreCase("windows")) { + System.setProperty("java.module.repository.shadowcopyfiles", "true"); + } + + Repository parent = sun.module.repository.RepositoryConfig.getApplicationRepository(); + Repository repository = Modules.newLocalRepository( + mTest.getName(), mTest.outputDirectory, null, parent); + + ModuleArchiveInfo[] maiList = new ModuleArchiveInfo[2]; + for (ModuleArchiveInfo mai : repository.list()) { + if (mai.getName().equals("m1")) { + maiList[0] = mai; + } else if (mai.getName().equals("m2")) { + maiList[1] = mai; + } + } + + JRepo jr = new JRepo(new PrintStream(System.out), resultStream, null); + + // An mtest's initial repository has modules already installed. + // Since here we test installation itself, do so in a new + // repository. + File repoDir = new File(mTest.outputDirectory, "installA"); + check(JamUtils.recursiveDelete(repoDir)); + check(repoDir.mkdirs()); + repository = Modules.newLocalRepository( + mTest.getName() + "installA", repoDir, null, parent); + + // Install m1 with the -q flag, which should succeed even though + // m2 is not installed. + try { + check(jr.run(getArgs( + "install -q -r " + + repoDir.getAbsolutePath() + " " + + maiList[0].getFileName()))); + pass(); + } catch (Throwable t) { + unexpected(t); + } + + // In a new repository, install m2 then m1: this should work: + repoDir = new File(mTest.outputDirectory, "installB"); + check(JamUtils.recursiveDelete(repoDir)); + check(repoDir.mkdirs()); + repository = Modules.newLocalRepository( + mTest.getName() + "installB", repoDir, null, parent); + ModuleArchiveInfo maiM2 = repository.install( + new File(maiList[1].getFileName()).toURI()); + check(maiM2 != null); + try { + check(jr.run(getArgs( + "install -r " + + repoDir.getAbsolutePath() + " " + + maiList[0].getFileName()))); + pass(); + } catch (Throwable t) { + unexpected(t); + } + + // In a new repository, install m1 without the -q flag: this + // should fail + repoDir = new File(mTest.outputDirectory, "installC"); + check(JamUtils.recursiveDelete(repoDir)); + check(repoDir.mkdirs()); + repository = Modules.newLocalRepository( + mTest.getName() + "installC", repoDir, null, parent); + try { + check(false == jr.run(getArgs( + "install -r " + + repoDir.getAbsolutePath() + " " + + maiList[0].getFileName()))); + checkOutput("no module definition in the repository can satisfy the import dependency ModuleDependency"); + } catch (Throwable t) { + pass(); + } + } + + static void checkOutput(String expected) { + String actual = baos.toString(); + check(actual.contains(expected)); + baos.reset(); + } + } + + //--------------------- Infrastructure --------------------------- + static volatile int passed = 0, failed = 0; + static boolean pass() {passed++; return true;} + static boolean fail() {failed++; Thread.dumpStack(); return false;} + static boolean fail(String msg) {System.err.println(msg); return fail();} + static void unexpected(Throwable t) {failed++; t.printStackTrace();} + static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} + static boolean equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) return pass(); + else return fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + try {realMain(args);} catch (Throwable t) {unexpected(t);} + System.out.println("\nPassed = " + passed + " failed = " + failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} --- /dev/null Wed Aug 6 09:30:39 2008 +++ new/test/java/module/modinit/JRepoValidateTest.java Wed Aug 6 09:30:44 2008 @@ -0,0 +1,171 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +import java.io.*; +import java.module.*; +import java.util.*; +import sun.module.JamUtils; +import sun.module.tools.JRepo; + +/** + * @test + * @compile -XDignore.symbol.file + * RunMTest.java + * classp/MainX.java + * JRepoValidateTest.java + * @run main/othervm + * -DTestDescriptionFactory.classname=JRepoValidateTest$MyFactory + * JRepoValidateTest + */ +public class JRepoValidateTest { + private static final boolean debug = Boolean.getBoolean("module.tools.debug"); + + private static final ByteArrayOutputStream outBytes = new ByteArrayOutputStream(); + private static final PrintStream outStream = new PrintStream(outBytes); + private static final ByteArrayOutputStream errBytes = new ByteArrayOutputStream(); + private static final PrintStream errStream = new PrintStream(errBytes); + + private static final String[] tests = { + "basic/import.mtest", + }; + + + public static void realMain(String args[]) throws Throwable { + RunMTest.main(tests); + } + + /** Return an array of Strings from the given String. */ + static String[] getArgs(String s) { + List args = new ArrayList(); + StringTokenizer st = new StringTokenizer(s); + while (st.hasMoreTokens()) { + String token = st.nextToken(); + if (debug) System.err.println("adding arg " + token); + args.add(token); + } + if (debug) System.err.println("args length is " + args.size()); + return args.toArray(new String[0]); + } + + public static class MyFactory extends RunMTest.TestDescriptionFactory { + + protected RunMTest.TestDescription doCreate(String name) { + return new MyTestDescription(name); + } + } + + public static class MyTestDescription extends RunMTest.TestDescription { + MyTestDescription(String name) { + super(name); + } + + protected void runTest(RunMTest mTest) throws Exception { + if (name.equals("m2")) { + // Only run this for m1. + return; + } + + Repository parent = sun.module.repository.RepositoryConfig.getApplicationRepository(); + Repository repository = Modules.newLocalRepository( + mTest.getName(), mTest.outputDirectory, null, parent); + + ModuleArchiveInfo[] maiList = new ModuleArchiveInfo[2]; + + for (ModuleArchiveInfo mai : repository.list()) { + if (mai.getName().equals("m1")) { + maiList[0] = mai; + } else if (mai.getName().equals("m2")) { + maiList[1] = mai; + } + } + + JRepo jr = new JRepo(outStream, errStream, null); + + // Check deep validation on module m1 which imports m2 + try { + check(jr.run(getArgs( + "validate -d -v -r " + + mTest.outputDirectory.getAbsolutePath() + " " + + maiList[0].getName()))); + checkOutput("passed deep validation", outBytes); + } catch (Throwable t) { + unexpected(t); + } + + // Check shallow validation on module m1 which imports m2 + try { + check(jr.run(getArgs( + "validate -v -r " + + mTest.outputDirectory.getAbsolutePath() + " " + + maiList[0].getName()))); + checkOutput("passed shallow validation", outBytes); + } catch (Throwable t) { + unexpected(t); + } + + // Install m1 into a new repository, and validate, expecting failure + File repoDir = new File(mTest.outputDirectory, "validateA"); + check(JamUtils.recursiveDelete(repoDir)); + check(repoDir.mkdirs()); + repository = Modules.newLocalRepository( + mTest.getName() + "validateA", repoDir, null, parent); + check(repository.install( + new File(maiList[0].getFileName()).toURI()) != null); + try { + check(false == jr.run(getArgs( + "validate -d -r " + + repoDir.getAbsolutePath() + " " + + maiList[0].getName()))); + checkOutput("no module definition in the repository can satisfy the import dependency ModuleDependency", errBytes); + } catch (Throwable t) { + unexpected(t); + } + } + + static void checkOutput(String expected, ByteArrayOutputStream baos) { + String actual = baos.toString(); + if (!check(actual.contains(expected))) { + System.err.println( + "Got\n'" + actual + "'\nbut expected\n'" + expected + "'"); + } + // Reset both streams after checking either one. + outBytes.reset(); + errBytes.reset(); + } + } + + //--------------------- Infrastructure --------------------------- + static volatile int passed = 0, failed = 0; + static boolean pass() {passed++; return true;} + static boolean fail() {failed++; Thread.dumpStack(); return false;} + static boolean fail(String msg) {System.err.println(msg); return fail();} + static void unexpected(Throwable t) {failed++; t.printStackTrace();} + static boolean check(boolean cond) {if (cond) pass(); else fail(); return cond;} + static boolean equal(Object x, Object y) { + if (x == null ? y == null : x.equals(y)) return pass(); + else return fail(x + " not equal to " + y);} + public static void main(String[] args) throws Throwable { + try {realMain(args);} catch (Throwable t) {unexpected(t);} + System.out.println("\nPassed = " + passed + " failed = " + failed); + if (failed > 0) throw new AssertionError("Some tests failed");} +} --- old/test/java/module/repository/LocalRepositoryTest.java Wed Aug 6 09:30:46 2008 +++ new/test/java/module/repository/LocalRepositoryTest.java Wed Aug 6 09:30:45 2008 @@ -68,11 +68,12 @@ } public static void realMain(String[] args) throws Throwable { + boolean onWindows = System.getProperty("os.platform").equalsIgnoreCase("windows"); // Enables shadow file copies in the repository if we're running // on Windows. This is to prevent file locking in the // source location. - if (System.getProperty("os.platform").equalsIgnoreCase("windows")) { + if (onWindows) { System.setProperty("java.module.repository.shadowcopyfiles", "true"); } @@ -177,7 +178,11 @@ repo.reload(); if (readOnlyChangeOK) { -// check(repo.isReadOnly()); + if (!onWindows) { + // I/O APIs are not completely honest about writability of + // directories on Windows; see 4939819 and 6728842. + check(repo.isReadOnly()); + } } // Check initial module is installed --- old/test/java/module/tools/jrepo/JRepoTest.java Wed Aug 6 09:30:47 2008 +++ new/test/java/module/tools/jrepo/JRepoTest.java Wed Aug 6 09:30:47 2008 @@ -49,7 +49,7 @@ */ check(!jr.run(new String[] {" "}) && usageOK(0)); check(!jr.run(getArgs("notASubCcommand")) && usageOK(0)); - check(!jr.run(getArgs("list -+")) && usageOK(1)); + check(!jr.run(getArgs("list -+")) && usageOK(-5)); /* * Do many tests on a LocalRepository, then a few on a URLRepository @@ -69,10 +69,10 @@ /* Check install command */ - check(!jr.run(getArgs("install")) && usageOK(0)); - check(!jr.run(getArgs("install repo module")) && usageOK(0)); + check(!jr.run(getArgs("install")) && usageOK(-3)); + check(!jr.run(getArgs("install repo module")) && usageOK(-3)); check(!jr.run(getArgs("install -r repoDoesNotExist module")) && errorOK(1)); - check(!jr.run(getArgs("install -p -r repoDoesNotExist module")) && errorOK(13)); + check(!jr.run(getArgs("install -p -r repoDoesNotExist module")) && errorOK(4)); // Create a directory for a local repository. File localRepoDir = new File(tmp, "JRepoTestLocalRepoDir"); @@ -87,7 +87,7 @@ // Verify silent output File jamFile = JamBuilder.createJam( - "jrepotest", "Example", "JRepoModuleA", "1.0", "platform", "arch", false, jamDir); + "jrepotest", "Example", "JRepoModuleA", "1.0", "platform1", "archA", false, jamDir); jamFile.deleteOnExit(); String jam = jamFile.getCanonicalPath(); check(jr.run(getArgs("install -r " + repo + " " + jam)) && outputOK(0)); @@ -94,7 +94,7 @@ // Verify verbose output jamFile = JamBuilder.createJam( - "jrepotest", "Example", "JRepoModuleB", "1.0", "platform", "arch", false, jamDir); + "jrepotest", "Example", "JRepoModuleB", "1.0", "platform2", "archB", false, jamDir); jamFile.deleteOnExit(); jam = jamFile.getCanonicalPath(); check(jr.run(getArgs("ins -v -r " + repo + " " + jam)) && outputOK(1)); @@ -101,12 +101,12 @@ // Verify multiple versions of same named module jamFile = JamBuilder.createJam( - "jrepotest", "Example", "JRepoModuleB", "2.0", "platform", "arch", false, jamDir); + "jrepotest", "Example", "JRepoModuleB", "2.0", "platform3", "archC", false, jamDir); jamFile.deleteOnExit(); jam = jamFile.getCanonicalPath(); check(jr.run(getArgs("install -v -r " + repo + " " + jam)) && outputOK(1)); jamFile = JamBuilder.createJam( - "jrepotest", "Example", "JRepoModuleB", "2.5", "platform", "arch", false, jamDir); + "jrepotest", "Example", "JRepoModuleB", "2.5", "platform4", "archD", false, jamDir); jamFile.deleteOnExit(); jam = jamFile.getCanonicalPath(); check(jr.run(getArgs("install -v -r " + repo + " " + jam)) && outputOK(1)); @@ -115,7 +115,7 @@ check(jr.run(getArgs("list -v -r " + repo)) && outputOK(6)); // Verify -p is not applicable to install - check(!jr.run(getArgs("install -p -r " + repo + " " + jam)) && usageOK(1)); + check(!jr.run(getArgs("install -p -r " + repo + " " + jam)) && usageOK(-4)); /* Check list command */ @@ -130,6 +130,9 @@ check(jr.run(getArgs("l -v -r " + repo)) && outputOK(6)); check(jr.run(getArgs("list -p -v -r " + repo)) && outputOK(28)); + // Check that repositories are shown in sorted order + check(jr.run(getArgs("l -v -r " + repo)) && sortedOK()); + // Nonexist things are not there check(!jr.run(getArgs("list ThisWillNotBeFound")) && outputOK(0)); check(!jr.run(getArgs("list -v ThisWillNotBeFound")) && errorOK(1)); @@ -154,10 +157,10 @@ /* Check uninstall command */ - check(!jr.run(getArgs("uninstall")) && usageOK(0)); - check(!jr.run(getArgs("uninstall repo MODULE")) && usageOK(0)); + check(!jr.run(getArgs("uninstall")) && usageOK(-3)); + check(!jr.run(getArgs("uninstall repo MODULE")) && usageOK(-3)); check(!jr.run(getArgs("uninstall -r repoDoesNotExist module")) && errorOK(1)); - check(!jr.run(getArgs("uninstall -p -r repoDoesNotExist module")) && errorOK(13)); + check(!jr.run(getArgs("uninstall -p -r repoDoesNotExist module")) && errorOK(4)); check(!jr.run(getArgs("uninstall -r " + repo + " Fred")) && errorOK(0)); check(!jr.run(getArgs("uninstall -v -r " + repo + " Fred")) && errorOK(1)); @@ -164,7 +167,7 @@ // Install one more module for tests below jamFile = JamBuilder.createJam( - "jrepotest", "Example", "JRepoModuleC", "1.0", "platform", "arch", false, jamDir); + "jrepotest", "Example", "JRepoModuleC", "1.0", "platform5", "archE", false, jamDir); jamFile.deleteOnExit(); jam = jamFile.getCanonicalPath(); check(jr.run(getArgs("ins -v -r " + repo + " " + jam)) && outputOK(1)); @@ -190,12 +193,12 @@ // Install more modules to verify -i works jamFile = JamBuilder.createJam( - "jrepotest", "Interact", "JRepoModuleD", "2.7", "platform", "arch", false, jamDir); + "jrepotest", "Interact", "JRepoModuleD", "2.7", "platform6", "archF", false, jamDir); jamFile.deleteOnExit(); jam = jamFile.getCanonicalPath(); check(jr.run(getArgs("install -v -r " + repo + " " + jam)) && outputOK(1)); jamFile = JamBuilder.createJam( - "jrepotest", "Interact", "JRepoModuleD", "3.1", "platform", "arch", false, jamDir); + "jrepotest", "Interact", "JRepoModuleD", "3.1", "platform7", "archG", false, jamDir); jamFile.deleteOnExit(); jam = jamFile.getCanonicalPath(); check(jr.run(getArgs("install -v -r " + repo + " " + jam)) && outputOK(1)); @@ -236,13 +239,13 @@ // Verify multiple versions of same named module jamFile = JamBuilder.createJam( - "jrepotest", "Example", "URLModuleX", "7.0", "platform", "arch", false, jamDir); + "jrepotest", "Example", "URLModuleX", "7.0", "platform8", "archH", false, jamDir); jamFile.deleteOnExit(); jam = jamFile.getCanonicalPath(); check(jr.run(getArgs("install -v -r " + urlRepoPath + " " + jam)) && outputOK(1)); jamFile = JamBuilder.createJam( - "jrepotest", "Example", "URLModuleX", "13.0", "platform", "arch", false, jamDir); + "jrepotest", "Example", "URLModuleX", "13.0", "platform9", "archI", false, jamDir); jamFile.deleteOnExit(); jam = jamFile.getCanonicalPath(); check(jr.run(getArgs("install -v -r " + urlRepoPath + " " + jam)) && outputOK(1)); @@ -312,10 +315,36 @@ /** Check that usage is provided as expected. */ static boolean usageOK(int len) throws Throwable { - // Add number of default lines of usage output to given value. - return checkOutput(12 + len, berr); + // If len < 0, use negative of that, else use len + default + return checkOutput(len < 0 ? -len : 20 + len, berr); } + /** Check that output is sorted, after skipping the firsts header line. */ + static boolean sortedOK() throws Throwable { + int numFailed = failed; + String allLines = bout.toString(); + if (debug) System.err.println("Checking sort order on '" + allLines + "'"); + BufferedReader r = new BufferedReader(new StringReader(allLines)); + check(r.readLine() != null); // Skip heading + List lines = new ArrayList(); + String s; + while ((s = r.readLine()) != null) { + check(!lines.contains(s)); + check(lines.add(s)); + } + String prev = lines.get(2); + for (int i = 3; i < lines.size(); i++) { + String cur = lines.get(i); + if (!check(prev.compareTo(cur) < 0)) { + System.err.println( + "'" + prev + "'\nis not less than \n'" + cur + "'"); + } + prev = cur; + } + bout.reset(); + return numFailed == failed; + } + //--------------------- Infrastructure --------------------------- static volatile int passed = 0, failed = 0; static boolean pass() {passed++; return true;}