--- old/src/share/classes/sun/module/repository/URLModuleInfo.java Tue Jul 8 18:11:42 2008 +++ new/src/share/classes/sun/module/repository/URLModuleInfo.java Tue Jul 8 18:11:42 2008 @@ -60,7 +60,7 @@ URLModuleInfo(String name, Version version, String platform, String arch, String path) { if ((platform == null ^ arch == null)) { throw new IllegalArgumentException( - "module platform and name must be either both provided, or neither provided"); + "module platform and arch must be either both provided, or neither provided"); } if (name == null) { --- /dev/null Tue Jul 8 18:11:44 2008 +++ new/src/share/classes/sun/module/tools/ImportTraverser.java Tue Jul 8 18:11:43 2008 @@ -0,0 +1,209 @@ +/* + * 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the LICENSE file that accompanied this code. + * + * 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. + */ + +package sun.module.tools; + +import java.io.File; +import java.io.IOException; +import java.module.Module; +import java.module.ModuleArchiveInfo; +import java.module.ModuleDefinition; +import java.module.ModuleInitializationException; +import java.module.Modules; +import java.module.Query; +import java.module.Repository; +import java.module.VersionConstraint; +import java.util.LinkedHashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import sun.module.JamUtils; + +/** + * Provides a means to visit each of a module's imports. + * @since 1.7 + */ +public class ImportTraverser implements Iterable { + private Set visited = new LinkedHashSet(); + + /** + * Invokes a visitor on each of the imports of the module identified by + * the parameters. The imports are visited in depth-first order with respect to + * the graph of interconnected modules. Note that the module identified + * by parameters is the first one visited. + * + * @param repo repository in which to find a named module + * @param name name of the module to find + * @param constraint version constraint on the module; may be null + * @throws ModuleInitializationException if the identified module cannot be + * instantiated + */ + public void visit(Repository repo, + String name, + VersionConstraint constraint) + throws ModuleInitializationException{ + + visit(repo, name, constraint, null, null); + } + + /** + * Invokes a visitor on each of the imports of the module identified by + * the parameters. The imports are visited in depth-first order with respect to + * the graph of interconnected modules. Note that the module identified + * by parameters is the first one visited. + * + * @param jamName name of a JAM file for which dependencies are returned + * @throws java.io.IOException if a Repository for the named JAM file cannot + * be created + * @throws ModuleInitializationException if the identified module cannot be + * instantiated + */ + public void visit(String jamName) + throws IOException, ModuleInitializationException { + + File repoDir = JamUtils.createTempDir(); + Repository repo = Modules.newLocalRepository("importvisitor", repoDir); + ModuleArchiveInfo mai = repo.install(new File(jamName).toURI().toURL()); + + visit(repo, mai.getName(), mai.getVersion().toVersionConstraint(), + mai.getPlatform(), mai.getArch()); + } + + /** + * Invokes a visitor on each of the imports of the module identified by + * the parameters. The imports are visited in depth-first order with respect to + * the graph of interconnected modules. Note that the module identified + * by parameters is the first one visited. + * + * @param repo repository in which to find a named module + * @param name name of the module to find + * @param constraint version constraint on the module; may be null + * @param platform platform of the module to find; may be null + * @param arch architecture of the module to find; may be null + * @throws ModuleInitializationException if the identified module cannot be + * instantiated + * @throws IllegalArgumentException if one of {@code platform, arch} is + * null and the other is not, or if the {@code repository} finds more than + * one module matching the given parameters. + */ + public void visit(Repository repo, + String name, VersionConstraint constraint, + String platform, String arch) + throws ModuleInitializationException { + + if (repo == null || name == null) { + throw new NullPointerException(); + } + + if (platform == null ^ arch == null) { + throw new IllegalArgumentException( + "module platform and arch must be either both null or both non-null"); + } + + Query q = Query.module( + name, constraint == null ? VersionConstraint.DEFAULT : constraint); + if (platform != null) { + q = Query.and( + q, Query.annotation(java.module.annotation.PlatformBinding.class)); + } + + List mdList = repo.find(q); + if (mdList.size() > 1) { + throw new IllegalArgumentException( + "more than one matching module found for name=" + name + + " version=" + constraint + + " platform=" + platform + " arch=" + arch); + } else if (mdList.size() == 1) { + Module m = mdList.get(0).getModuleInstance(); + init(m); + visit(repo, m); + } + } + + public void visit(Repository repo, + Module module) { + + if (visited.contains(module)) { + return; + } + visited.add(module); + if (preVisit(module)) { + List imported = module.getImportedModules(); + for (Module m : imported) { + visit(m); + visit(repo, m); + } + } + postVisit(module); + } + + /** + * @return an unmodifiable list of the {@code Module} instances which have + * been visited; note that the list is ordered by the depth-first + * traversal of module imports. + */ + public Iterator iterator() { + return visited.iterator(); + } + + /** + * @param m {@code Module} possibly visited by the traverser + * @return true if the given {@code Module} has been visited, false + * otherwise + */ + protected boolean visited(Module m) { + return visited.contains(m); + } + + /** + * Invoked with the {@code Module} identified by the client-called {@code + * visit} method before any other {@code Module} instances are visited. + * @param m First {@code Module} visited by the traverser + */ + protected void init(Module m) { } + + /** + * Invoked on a {@code Module} instance before its imports are visited. + * @param m {@code Module} which imports the module about to be visited by + * {@link #visit(Module)} + * @return true if the {@code Module} should be visited, false otherwise. + */ + protected boolean preVisit(Module m) { + return true; + } + + /** + * Invoked on a {@code Module} instance as it is visited + * @param m {@code Module} being visited + */ + protected void visit(Module m) { } + + /** + * Invoked on a {@code Module} instance after its imports are visited. + * @param m {@code Module} which imports the module that was just visited by + * {@link #visit(Module)} + */ + protected void postVisit(Module m) { } +} --- old/src/share/classes/sun/module/tools/JRepo.java Tue Jul 8 18:11:44 2008 +++ new/src/share/classes/sun/module/tools/JRepo.java Tue Jul 8 18:11:44 2008 @@ -30,6 +30,7 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; +import java.io.OutputStream; import java.io.PrintStream; import java.io.PrintWriter; import java.io.StringWriter; @@ -36,16 +37,20 @@ import java.net.MalformedURLException; import java.net.URL; import java.module.*; +import java.module.annotation.ImportPolicyClass; import java.security.AccessController; +import java.security.PrivilegedAction; import java.text.DateFormat; import java.util.Arrays; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import sun.module.JamUtils; +import java.util.Set; import sun.module.repository.RepositoryConfig; import sun.security.action.GetPropertyAction; import sun.tools.jar.CommandLine; @@ -77,12 +82,18 @@ /** Usage message created from each command's usage. */ private static String usage = null; - /** Format for module name & version. */ + /** Format for ModuleArchiveInfo name & version. */ private static final String MAIFormat = "%-20s %-20s"; - /** Format for additional/verbose module information. */ + /** Format for additional/verbose ModuleArchiveInfo details. */ private static final String MAIFormatVerbose = " %-9s %-7s %-17s %s"; + /** Format for ModuleDefinition name & version. */ + private static final String MDFormat = "%s-%s"; + + /** Format for additional/verbose ModuleDefinition details. */ + private static final String MDFormatVerbose = " %s"; + /** String containing column headings for name & version. */ private static final String maiHeading; @@ -98,6 +109,12 @@ /** Location of repository; if not given uses system repository. */ private static final RepositoryFlag repositoryFlag = new RepositoryFlag(); + /** 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(); + /** Contains the flags that are common to all commands. */ private static final HashMap commonFlags = new HashMap(); @@ -117,6 +134,10 @@ maiHeadingVerbose = sw.toString(); } + public JRepo(OutputStream out, OutputStream err, BufferedReader reader) { + this(new PrintStream(out), new PrintStream(err), reader); + } + public JRepo(PrintStream out, PrintStream err, BufferedReader reader) { msg = new Messenger("jrepo", out, err, reader); synchronized(commands) { @@ -124,6 +145,7 @@ new ListCommand().register(commands); new InstallCommand().register(commands); new UninstallCommand().register(commands); + new DependenciesCommand().register(commands); } } reset(); @@ -187,7 +209,8 @@ "jrepo", f.getCanonicalFile()); } else { - throw new IOException("Cannot access repository at " + repositoryLocation); + throw new IOException("Cannot access repository at " // XXX i18n + + repositoryLocation); } } } @@ -260,18 +283,18 @@ /** Returns a user-grokkable description of the repository. */ - private String getRepositoryText(Repository repo) { + private static String getRepositoryText(Repository repo) { String rc; URL u = repo.getSourceLocation(); if (u == null) { rc = "Bootstrap repository"; } else { - rc = "Repository " + u.toExternalForm(); + rc = "Repository '" + repo.getName() + "' at " + u.toExternalForm(); } return rc; } - private String getMAIText(ModuleArchiveInfo mai) { + private static String getMAIText(ModuleArchiveInfo mai) { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); pw.printf(MAIFormat, mai.getName(), mai.getVersion()); @@ -291,6 +314,22 @@ return sw.toString(); } + private static String getMText(Module m) { + return getMDText(m.getModuleDefinition()); + } + + private static String getMDText(ModuleDefinition md) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + pw.printf(MDFormat, md.getName(), md.getVersion()); + if (verboseFlag.isEnabled()) { + URL srcLoc = md.getRepository().getSourceLocation(); + pw.printf(MDFormatVerbose, + srcLoc == null ? "bootstrap" : srcLoc.toString()); + } + return sw.toString(); + } + void usageError() { usageError(msg); } @@ -298,7 +337,7 @@ static void usageError(Messenger msg) { if (usage == null) { StringBuilder ub = new StringBuilder( - "Usage: jrepo \nwhere includes:"); + "Usage: jrepo \nwhere includes:"); // XXX i18n for (Command c : commands.values()) { String u = c.usage(); if (u != null) { @@ -313,7 +352,7 @@ /** * Represents a flag given on the command line. Flags always are 2 * characters long, and start with a '-'. They can have additional - * associated arguments (cf RepositoryFlag). + * associated arguments (cf {@link #RepositoryFlag}). */ private static class Flag { private final char name; @@ -336,7 +375,7 @@ } /** @return the number of arguments consumed by this Flag. */ - int set(String[] args, int pos) { + int set(String[] args, int pos) throws IllegalArgumentException { enabled = true; return 1; } @@ -361,7 +400,8 @@ super('r'); } - int set(String[] args, int pos) { + @Override + int set(String[] args, int pos) throws IllegalArgumentException { int rc = super.set(args, pos); location = args[pos + 1]; return rc + 1; @@ -371,6 +411,7 @@ return location; } + @Override void reset() { super.reset(); location = null; @@ -377,6 +418,43 @@ } } + 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. @@ -424,7 +502,8 @@ i += numConsumed; // Increases at each iteration. rc += numConsumed; // Increases only when the arg is a flag. } else { - throw new IllegalArgumentException("unrecognized flag: " + args[i]); + throw new IllegalArgumentException("unrecognized flag: " // XXX i18n + + args[i]); } } else { i++; @@ -441,6 +520,7 @@ */ abstract String usage(); + @Override public String toString() { return name; } /** @@ -453,9 +533,9 @@ abstract class RepositoryVisitor { abstract void doit(Repository repo, Messenger msg); - void doBefore(Messenger msg) { } + void preVisit(Messenger msg) { } - void doAfter(Messenger msg) { } + void postVisit(Messenger msg) { } final void run(Repository repo, Messenger msg) { visit(repo, msg); @@ -466,13 +546,179 @@ if (parent != null) { visit(parent, msg); } - doBefore(msg); + preVisit(msg); doit(repo, msg); - doAfter(msg); + postVisit(msg); } } } + + /** Lists dependencies of a module. */ + private class DependenciesCommand extends Command { + @SuppressWarnings("unchecked") + private final Map myFlags = (Map) commonFlags.clone(); + + private String name; + private Version version; + + // For printing module name, version. + private String moduleString; + + DependenciesCommand() { + super("dependencies"); + javaseFlag.register(myFlags); + bindingFlag.register(myFlags); + } + + @Override + void reset() { + javaseFlag.reset(); + bindingFlag.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 + + " plat: " + bindingFlag.getPlatform() + + " arch: " + bindingFlag.getArch()); + return true; + } + + boolean run(Repository repo, Messenger msg) { + boolean rc = true; + if (name != null) { + VersionConstraint vc = (version == null + ? VersionConstraint.DEFAULT + : version.toVersionConstraint()); + printHeader(name, vc); + rc = depend(repo, name, vc, + bindingFlag.getPlatform(), bindingFlag.getArch()); + printTrailer(null); + } else { + for (ModuleArchiveInfo mai : repo.list()) { + reset(); + String maiName = mai.getName(); + VersionConstraint vc = mai.getVersion().toVersionConstraint(); + printHeader(maiName, vc); + rc &= depend(repo, maiName, vc, + mai.getPlatform(), mai.getArch()); + printTrailer("\n"); + } + } + return rc; + } + + boolean depend(Repository repo, String name, VersionConstraint constraint, + String platform, String arch) { + ImportTraverser iv = new JRepoImportTraverser(msg); + try { + iv.visit(repo, name, constraint, platform, arch); + for (Module dep : iv) { + // If any modules were found, traversal was successful + return true; + } + if (verboseFlag.isEnabled()) { + msg.error("Cannot find module " + name + " in " // XXX i18n + + getRepositoryText(repo)); + } + return false; + } catch (ModuleInitializationException ex) { + msg.error("Cannot instantiate module for " + name // XXX i18n + + ": " + ex); + return false; + } + } + + + void printHeader(String name, VersionConstraint vc) { + if (verboseFlag.isEnabled()) { + msg.println("Dependencies for " // XXXi18n + + name + "-" + vc + ":"); + } + } + + void printTrailer(String s) { + if (verboseFlag.isEnabled()) { + msg.print(s); + } + } + + // TBD support platform binding. Ideally, want to allow specifying + // version, or binding, or both. + String usage() { + return "dependencies [-v] [-r repositoryLocation] [-b platform-arch]"// XXX i18n + + " [moduleName [moduleVersion] ]\n" + + " Lists all modules on which identified modules depend.\n" + + " If no moduleName is given, lists dependencies of all" + + " modules in the repository."; + } + } + + + private static class JRepoImportTraverser extends ImportTraverser { + private final Messenger msg; + + private String indent = ""; + + JRepoImportTraverser(Messenger msg) { + this.msg = msg; + } + + @Override + protected void init(Module m) { + printModule(m); + } + + @Override + protected boolean preVisit(Module m) { + if (javaseFlag.isEnabled() == false + && "java.se".equals(m.getModuleDefinition().getName())) { + return false; + } else { + indent += " "; + return true; + } + } + + @Override + protected void visit(Module m) { + printModule(m); + } + + @Override + protected void postVisit(Module m) { + if (javaseFlag.isEnabled() == false + && "java.se".equals(m.getModuleDefinition().getName())) { + // empty + } else { + indent = indent.substring(4); + } + } + + void printModule(Module m) { + msg.println(indent + getMText(m)); + } + } + /** Installs a JAM into a repository. */ private class InstallCommand extends Command { private String jamName; @@ -528,7 +774,7 @@ } String usage() { - return "install [-v] -r repositoryLocation jamFile | jamURL\n" + return "install [-v] -r repositoryLocation jamFile | jamURL\n" // XXX i18n + " installs a module into a repository"; } } @@ -554,6 +800,7 @@ interactiveFlag.register(myFlags); } + @Override void reset() { version = null; platformBinding = null; @@ -561,6 +808,7 @@ interactiveFlag.reset(); } + @Override int parseFlags(String[] args, Map flags) { return super.parseFlags(args, myFlags); } @@ -647,7 +895,8 @@ } String usage() { - return "uninstall [-v] [-f | -i] -r repositoryLocation moduleName [moduleVersion] [modulePlatformBinding]\n" + return "uninstall [-v] [-f | -i] -r repositoryLocation moduleName"// XXX i18n + + " [moduleVersion] [modulePlatformBinding]\n" + " removes a module from a repository, along with associated files."; } @@ -659,9 +908,9 @@ rc = repo.uninstall(mai); if (verboseFlag.isEnabled()) { if (rc) { - msg.println("Uninstalled " + getMAIText(mai)); + msg.println("Uninstalled " + getMAIText(mai)); // XXX i18n } else { - msg.error("Failed to uninstall " + getMAIText(mai)); + msg.error("Failed to uninstall " + getMAIText(mai)); // XXX i18n } } } catch (Exception ex) { @@ -769,10 +1018,12 @@ parentFlag.register(myFlags); } + @Override void reset() { parentFlag.reset(); } + @Override int parseFlags(String[] args, Map flags) { return super.parseFlags(args, myFlags); } @@ -795,9 +1046,11 @@ boolean found = visitor.wasFound(); if (verboseFlag.isEnabled() && !found) { if (moduleName != null) { - msg.error("Could not find module name starting with '" + moduleName + "'"); + msg.error("Could not find module name starting with '"// + // XXX i18n + + moduleName + "'"); } else { - msg.error("Could not find any modules"); + msg.error("Could not find any modules"); // XXX i18n } } return found; @@ -804,7 +1057,8 @@ } String usage() { - return "list [-v] [-p] [-r repositoryLocation] moduleName\n" + return "list [-v] [-p] [-r repositoryLocation] moduleName\n"// + // XXX i18n + " lists the modules in the repository"; } @@ -815,15 +1069,21 @@ void doit(Repository repo, Messenger msg) { boolean printedHeader = false; - for (ModuleArchiveInfo mai : repo.list()) { - if (moduleName == null || mai.getName().startsWith(moduleName)) { - if (!printedHeader) { - msg.println(getRepositoryText(repo)); - msg.println(verboseFlag.isEnabled() ? maiHeadingVerbose : maiHeading); - printedHeader = true; + List maiList = repo.list(); + if (maiList.size() == 0 && verboseFlag.isEnabled()) { + msg.println(getRepositoryText(repo)); + msg.println(" empty"); + } else { + for (ModuleArchiveInfo mai : repo.list()) { + if (moduleName == null || mai.getName().startsWith(moduleName)) { + if (!printedHeader) { + msg.println(getRepositoryText(repo)); + msg.println(verboseFlag.isEnabled() ? maiHeadingVerbose : maiHeading); + printedHeader = true; + } + msg.println(getMAIText(mai)); + found = true; } - msg.println(getMAIText(mai)); - found = true; } } } --- /dev/null Tue Jul 8 18:11:46 2008 +++ new/test/java/module/modinit/JRepoDependenciesTest.java Tue Jul 8 18:11:46 2008 @@ -0,0 +1,183 @@ +/* + * 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.tools.JRepo; + +/** + * @test + * @library ../tools ../tools/jrepo + * @compile -XDignore.symbol.file + * RunMTest.java + * classp/MainX.java + * JRepoDependenciesTest.java + * @run main/othervm + * -DTestDescriptionFactory.classname=JRepoDependenciesTest$MyFactory + * JRepoDependenciesTest + */ +public class JRepoDependenciesTest { + private static final boolean debug = Boolean.getBoolean("module.tools.debug"); + + private static PrintStream outStream = null; + + private static final String[] tests = { + "basic/import.mtest", + "circular/circular2.mtest", + "importpolicy/import1.mtest", + "importpolicy/optional1.mtest", + "importpolicy/optional2.mtest", + "importpolicy/recurse1.mtest", + "importpolicy/version1.mtest", + "importpolicy/version3.mtest", + "optional/basic.mtest", + "optional/indirect.mtest", + "optional/missing.mtest", + "validation/shadow4.mtest", + "version/version3.mtest" + }; + + private static PrintStream resultStream; + + public static void realMain(String args[]) throws Throwable { + if (args.length == 0) { + args = tests; + } + File resultFile = new File( + System.getProperty("test.scratch", "."), "JRepoDepOut.txt"); + resultFile.delete(); + resultStream = new PrintStream(new FileOutputStream(resultFile)); + RunMTest.main(args); + resultStream.close(); + compare(resultFile); + } + + static void compare(File resultFile) throws Throwable { + File goldFile = new File( + System.getProperty("test.src", "."), "JRepoDepGold.txt"); + if (goldFile.exists()) { + BufferedReader goldReader = new BufferedReader( + new FileReader(goldFile)); + BufferedReader resultReader = new BufferedReader( + new FileReader(resultFile)); + String goldLine = null; + String resultLine = null; + while ((goldLine = goldReader.readLine()) != null) { + resultLine = resultReader.readLine(); + if (!goldLine.equals(resultLine)) { + System.err.println( + "MISMATCH: expected\n\t" + goldLine + + "but got\n\t" + resultLine); + fail(); + break; + } + } + goldReader.close(); + resultReader.close(); + pass(); + } else { + System.err.println( + "Error: JRepoDepGold.txt does not exist: " + + "not comparing actual v. expected results"); + fail(); + } + } + + /** 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 { + Repository parent = sun.module.repository.RepositoryConfig.getSystemRepository(); + Repository repository = Modules.newLocalRepository( + parent, mTest.getName(), mTest.outputDirectory); + + File file = mTest.file; + char SEP = File.separatorChar; + String mString = SEP + "mtest" + SEP; + String cPath = file.getCanonicalPath(); + int k = cPath.lastIndexOf(mString); + String subdir; + if (k == -1) { + subdir = file.getName(); + } else { + subdir = cPath.substring(k + mString.length()); + } + resultStream.println(">>>Test " + subdir); + + JRepo jr = new JRepo(resultStream, new PrintStream(System.err), null); + + try { + check(jr.run(getArgs( + "dependencies -r " + + mTest.outputDirectory.getAbsolutePath() + " " + + name))); + } catch (Exception ex) { + if (ex instanceof ModuleInitializationException) { + // Ignore: Assume this is expected or that it is a new error + // that will be diagnosed when by examining the modinit tests + // themselves. + } else { + unexpected(ex); + } + } + } + } + + //--------------------- 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 Tue Jul 8 18:11:47 2008 +++ new/test/java/module/modinit/JRepoDepGold.txt Tue Jul 8 18:11:46 2008 @@ -0,0 +1,114 @@ +>>>Test basic/import.mtest +m1-1.0 + java.se-1.7 + m2-1.0 + java.se-1.7 +>>>Test basic/import.mtest +m2-1.0 + java.se-1.7 +>>>Test circular/circular2.mtest +m1-1.0 + java.se-1.7 + m2-1.0 + java.se-1.7 + m3-1.0 + java.se-1.7 + m1-1.0 +>>>Test circular/circular2.mtest +m2-1.0 + java.se-1.7 + m3-1.0 + java.se-1.7 + m1-1.0 + java.se-1.7 + m2-1.0 +>>>Test circular/circular2.mtest +m3-1.0 + java.se-1.7 + m1-1.0 + java.se-1.7 + m2-1.0 + java.se-1.7 + m3-1.0 +>>>Test importpolicy/import1.mtest +m1-0.0-default + java.se-1.7 + m2-0.0-default + java.se-1.7 +>>>Test importpolicy/optional1.mtest +m1-0.0-default + java.se-1.7 + m2-0.0-default + java.se-1.7 + m3-0.0-default + java.se-1.7 +>>>Test importpolicy/optional2.mtest +m1-0.0-default + java.se-1.7 + m3-0.0-default + java.se-1.7 +>>>Test importpolicy/recurse1.mtest +m1-0.0-default + java.se-1.7 + m2-0.0-default + java.se-1.7 +>>>Test importpolicy/version1.mtest +m1-0.0-default + java.se-1.7 + m2-1.2 + java.se-1.7 +>>>Test importpolicy/version3.mtest +m1-0.0-default + java.se-1.7 + m2-1.2 + java.se-1.7 +>>>Test optional/basic.mtest +m1-0.0-default + java.se-1.7 + m2-0.0-default + java.se-1.7 + m3-0.0-default + java.se-1.7 + m4-0.0-default + java.se-1.7 +>>>Test optional/indirect.mtest +m1-0.0-default + java.se-1.7 + m2-0.0-default + java.se-1.7 + m3-0.0-default + java.se-1.7 + m4-0.0-default + java.se-1.7 +>>>Test optional/missing.mtest +m1-0.0-default + java.se-1.7 + m2-0.0-default + java.se-1.7 + m4-0.0-default + java.se-1.7 +>>>Test validation/shadow4.mtest +m1-0.0-default + java.se-1.7 + m2-0.0-default + java.se-1.7 + m3-0.0-default + java.se-1.7 +>>>Test validation/shadow4.mtest +m2-0.0-default + java.se-1.7 + m3-0.0-default + java.se-1.7 +>>>Test validation/shadow4.mtest +m3-0.0-default + java.se-1.7 +>>>Test version/version3.mtest +m1-1.1 + java.se-1.7 + m2-0.0-default + java.se-1.7 + m4-1.6 + java.se-1.7 + m3-2.0 + java.se-1.7 + m4-1.6 --- old/test/java/module/modinit/RunMTest.java Tue Jul 8 18:11:48 2008 +++ new/test/java/module/modinit/RunMTest.java Tue Jul 8 18:11:47 2008 @@ -61,8 +61,8 @@ defaultProperties.put("header", WARNING_HEADER); } - private final File file; - private final File outputDirectory; + protected final File file; + protected final File outputDirectory; private final List modules; private final List tests; @@ -77,7 +77,7 @@ } else { subdir = cPath.substring(k + mString.length()); } - System.out.println(">>> Test " + subdir); + System.out.println(">>>Test " + subdir); outputDirectory = new File(baseDirectory, subdir); if (outputDirectory.exists()) { recursiveDelete(outputDirectory); @@ -87,7 +87,7 @@ parse(); } - private String getName() { + protected String getName() { return file.getName(); } @@ -310,16 +310,49 @@ } } - private static class TestDescription { - private final String name; - private String result; + // Abstracting the creation of TestDescription instances to a factory + // allows other tests to override TestDescription.runTest. + // + public static class TestDescriptionFactory { + private static TestDescriptionFactory instance; + + static TestDescription create(String name) { + return getInstance().doCreate(name); + } - private TestDescription(String name) { + private static TestDescriptionFactory getInstance() { + if (instance == null) { + String factName = System.getProperty("TestDescriptionFactory.classname"); + if (factName != null) { + try { + Class clazz = Class.forName(factName); + instance = (TestDescriptionFactory) clazz.newInstance(); + } catch (Throwable t) { + throw new RuntimeException(t); + } + } else { + instance = new TestDescriptionFactory(); + } + } + return instance; + } + + protected TestDescription doCreate(String name) { + return new TestDescription(name); + } + } + + public static class TestDescription { + + protected final String name; + protected String result; + + TestDescription(String name) { this.name = name; } - private void runTest(RunMTest mTest) throws Exception { + protected void runTest(RunMTest mTest) throws Exception { System.out.println("> Running test " + name + "..."); Repository parent = sun.module.repository.RepositoryConfig.getSystemRepository(); Repository repository = Modules.newLocalRepository(parent, mTest.getName(), mTest.outputDirectory); @@ -360,7 +393,7 @@ private TestDescription parseTest(String header, BufferedReader reader) throws IOException { String[] s = header.split(" "); String name = s[s.length - 1]; - TestDescription test = new TestDescription(name); + TestDescription test = TestDescriptionFactory.create(name); while (true) { String line = getLine(reader); if (line == null) {