diff options
author | wisberg <wisberg> | 2003-10-29 08:56:38 +0000 |
---|---|---|
committer | wisberg <wisberg> | 2003-10-29 08:56:38 +0000 |
commit | ed96631f11efc4895a90a68f86f0e75eef8e9e2d (patch) | |
tree | 89a9d5fb905a2e87b41b4480d45651c84c9e5602 /testing | |
parent | 7a01c23476bda528f577b9fe4861f40ad1086381 (diff) | |
download | aspectj-ed96631f11efc4895a90a68f86f0e75eef8e9e2d.tar.gz aspectj-ed96631f11efc4895a90a68f86f0e75eef8e9e2d.zip |
revamped option handling: n-element options, collision/conflict detection/resolution via forcing as an attribute, option families. More compiler options actually handled, and error messages for unhandled options.
Diffstat (limited to 'testing')
12 files changed, 3928 insertions, 609 deletions
diff --git a/testing/src/org/aspectj/testing/harness/bridge/AbstractRunSpec.java b/testing/src/org/aspectj/testing/harness/bridge/AbstractRunSpec.java index 8339a25ad..b1e3051e1 100644 --- a/testing/src/org/aspectj/testing/harness/bridge/AbstractRunSpec.java +++ b/testing/src/org/aspectj/testing/harness/bridge/AbstractRunSpec.java @@ -30,7 +30,10 @@ import org.aspectj.bridge.ISourceLocation; import org.aspectj.bridge.MessageHandler; import org.aspectj.bridge.MessageUtil; import org.aspectj.testing.run.IRunIterator; +import org.aspectj.testing.util.*; import org.aspectj.testing.util.BridgeUtil; +import org.aspectj.testing.util.options.*; +import org.aspectj.testing.util.options.Option.InvalidInputException; import org.aspectj.testing.xml.IXmlWritable; import org.aspectj.testing.xml.SoftMessage; import org.aspectj.testing.xml.XMLWriter; @@ -956,6 +959,66 @@ abstract public class AbstractRunSpec implements IRunSpec { // XXX use MessageHa } /** + * Return any parent option accepted by validOptions, + * optionally removing the parent option. + * @param validOptions String[] of options to extract + * @param remove if true, then remove any parent option matched + * @return String[] containing any validOptions[i] in parentOptions + * + */ + public Values extractOptions( + Options validOptions, + boolean remove, + StringBuffer errors) { + Values result = Values.EMPTY; + if (null == errors) { + errors = new StringBuffer(); + } + if (null == validOptions) { + errors.append("null options"); + return result; + } + if (LangUtil.isEmpty(parentOptions)) { + return result; + } + boolean haveOption = false; + String[] parents = (String[]) parentOptions.toArray(new String[0]); + try { + result = validOptions.acceptInput(parents); + } catch (InvalidInputException e) { + errors.append(e.getFullMessage()); + return result; + } + if (remove) { + Option.Value[] values = result.asArray(); + for (int i = 0; i < values.length; i++) { + Option.Value value = values[i]; + if (null == value) { + continue; + } + final int max = i + value.option.numArguments(); + if (max > i) { + if (max >= parents.length) { + errors.append("expecting more args for " + + value.option + + " at [" + + i + + "]: " + + Arrays.asList(parents)); + return result; + } + // XXX verify + for (int j = i; j < max ; j++) { + parentOptions.remove(parents[j]); + } + i = max-1; + } + } + } + return result; + } + + /** * Return any parent option which has one of validOptions as a prefix, * optionally absorbing (removing) the parent option. * @param validOptions String[] of options to extract diff --git a/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java b/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java index 172f0de74..0f0385ac2 100644 --- a/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java +++ b/testing/src/org/aspectj/testing/harness/bridge/CompilerRun.java @@ -1,5 +1,6 @@ /* ******************************************************************* - * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC). + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC), + * 2003 Contributors. * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Common Public License v1.0 @@ -8,34 +9,22 @@ * * Contributors: * Xerox/PARC initial implementation + * Wes Isberg 2003 updates * ******************************************************************/ package org.aspectj.testing.harness.bridge; -import org.aspectj.bridge.ICommand; -import org.aspectj.bridge.IMessage; -import org.aspectj.bridge.IMessageHandler; -import org.aspectj.bridge.MessageUtil; -import org.aspectj.bridge.ReflectionFactory; +import java.io.*; +import java.util.*; + +import org.aspectj.bridge.*; import org.aspectj.testing.ajde.CompileCommand; -import org.aspectj.testing.run.IRunIterator; -import org.aspectj.testing.run.IRunStatus; -import org.aspectj.testing.run.WrappedRunIterator; +import org.aspectj.testing.run.*; import org.aspectj.testing.taskdefs.AjcTaskCompileCommand; -import org.aspectj.testing.xml.SoftMessage; -import org.aspectj.testing.xml.XMLWriter; -import org.aspectj.util.FileUtil; -import org.aspectj.util.LangUtil; - -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; +import org.aspectj.testing.util.options.*; +import org.aspectj.testing.util.options.Option.*; +import org.aspectj.testing.xml.*; +import org.aspectj.util.*; /** * Run the compiler once. @@ -62,40 +51,37 @@ import java.util.ListIterator; * </ul> */ public class CompilerRun implements IAjcRun { - static final String BUILDER_COMPILER = - "org.aspectj.ajdt.internal.core.builder.Builder.Command"; - static final String AJDE_COMPILER = CompileCommand.class.getName(); - static final String AJCTASK_COMPILER - = AjcTaskCompileCommand.class.getName(); -// static final String JAVAC_COMPILER -// = JavacCompileCommand.class.getName(); - - static final String[] RA_String = new String[0]; - + // static final String JAVAC_COMPILER + // = JavacCompileCommand.class.getName(); + + static final String[] RA_String = new String[0]; + static final String[] JAR_SUFFIXES = new String[] { ".jar", ".zip" }; - static final String[] SOURCE_SUFFIXES - = (String[]) FileUtil.SOURCE_SUFFIXES.toArray(new String[0]); - + static final String[] SOURCE_SUFFIXES = + (String[]) FileUtil.SOURCE_SUFFIXES.toArray(new String[0]); + /** specifications, set on construction */ Spec spec; //------------ calculated during setup - /** get shared stuff during setup */ + /** get shared stuff during setup */ Sandbox sandbox; - + /** * During run, these String are passed as the source and arg files to compile. * The list is set up in setupAjcRun(..), when arg files are prefixed with "@". */ - final List /*String*/ arguments; - + final List /*String*/ + arguments; + /** * During run, these String are collapsed and passed as the injar option. * The list is set up in setupAjcRun(..). */ - final List /*String*/ injars; - + final List /*String*/ + injars; + private CompilerRun(Spec spec) { if (null == spec) { throw new IllegalArgumentException("null spec"); @@ -103,8 +89,8 @@ public class CompilerRun implements IAjcRun { this.spec = spec; arguments = new ArrayList(); injars = new ArrayList(); - } - + } + /** * This checks that the spec is reasonable and does setup: * <ul> @@ -124,72 +110,98 @@ public class CompilerRun implements IAjcRun { * for incremental tests. Note that (as of this writing) the * compiler only handles source roots for incremental tests. * @param classesDir the File - * @see org.aspectj.testing.harness.bridge.AjcTest.IAjcRun#setup(File, File) + * @see org.aspectj.testing.harness.bridge.AjcTest.IAjcRun#setup(File, File) * @throws AbortException containing IOException or IllegalArgumentException * if the staging operations fail - */ - public boolean setupAjcRun(Sandbox sandbox, Validator validator) { + */ + public boolean setupAjcRun(Sandbox sandbox, Validator validator) { if (!validator.nullcheck(spec.getOptionsArray(), "localOptions") || !validator.nullcheck(sandbox, "sandbox") || !validator.nullcheck(spec.compiler, "compilerName") || !validator.canRead(Globals.F_aspectjrt_jar, "aspectjrt.jar") - || !validator.canRead(Globals.F_testingclient_jar, "testing-client.jar") - ) { + || !validator.canRead( + Globals.F_testingclient_jar, + "testing-client.jar")) { return false; } - + this.sandbox = sandbox; - + String rdir = spec.testSrcDirOffset; File testBaseSrcDir; if ((null == rdir) || (0 == rdir.length())) { testBaseSrcDir = sandbox.testBaseDir; } else { - testBaseSrcDir = new File(sandbox.testBaseDir, rdir); // XXX what if rdir is two levels deep? - if (!validator.canReadDir(testBaseSrcDir, "sandbox.testBaseSrcDir")) { + testBaseSrcDir = new File(sandbox.testBaseDir, rdir); + // XXX what if rdir is two levels deep? + if (!validator + .canReadDir(testBaseSrcDir, "sandbox.testBaseSrcDir")) { return false; } } sandbox.setTestBaseSrcDir(testBaseSrcDir, this); - - + // Sources come as relative paths - check read, copy if staging. // This renders paths absolute before run(RunStatusI) is called. // For a compile run to support relative paths + source base, // change so the run calculates the paths (differently when staging) - final String[] injarPaths; + final String[] injarPaths; final String[] srcPaths; - { + { final String[] paths = spec.getPathsArray(); - srcPaths = LangUtil.endsWith(paths, CompilerRun.SOURCE_SUFFIXES, true); - injarPaths = LangUtil.endsWith(paths, CompilerRun.JAR_SUFFIXES, true); - } + srcPaths = + LangUtil.endsWith( + paths, + CompilerRun.SOURCE_SUFFIXES, + true); + injarPaths = + LangUtil.endsWith(paths, CompilerRun.JAR_SUFFIXES, true); + } // validate readable for sources if (!spec.badInput) { if (!validator.canRead(testBaseSrcDir, srcPaths, "sources") || !validator.canRead(testBaseSrcDir, injarPaths, "injars") - || !validator.canRead(testBaseSrcDir, spec.argfiles, "argfiles") - || !validator.canRead(testBaseSrcDir, spec.classpath, "classpath") - || !validator.canRead(testBaseSrcDir, spec.aspectpath, "aspectpath") - || !validator.canRead(testBaseSrcDir, spec.sourceroots, "sourceroots") - ) { + || !validator.canRead( + testBaseSrcDir, + spec.argfiles, + "argfiles") + || !validator.canRead( + testBaseSrcDir, + spec.classpath, + "classpath") + || !validator.canRead( + testBaseSrcDir, + spec.aspectpath, + "aspectpath") + || !validator.canRead( + testBaseSrcDir, + spec.sourceroots, + "sourceroots")) { return false; } } - - int numSources = srcPaths.length + injarPaths.length - + spec.argfiles.length + spec.sourceroots.length; + + int numSources = + srcPaths.length + + injarPaths.length + + spec.argfiles.length + + spec.sourceroots.length; if (!spec.badInput && (numSources < 1)) { - validator.fail("no input jars, arg files, or source files or roots"); - return false; - } - - final File[] argFiles = FileUtil.getBaseDirFiles(testBaseSrcDir, spec.argfiles); - final File[] injarFiles = FileUtil.getBaseDirFiles(testBaseSrcDir, injarPaths); - final File[] aspectFiles = FileUtil.getBaseDirFiles(testBaseSrcDir, spec.aspectpath); - final File[] classFiles = FileUtil.getBaseDirFiles(testBaseSrcDir, spec.classpath); + validator.fail( + "no input jars, arg files, or source files or roots"); + return false; + } + + final File[] argFiles = + FileUtil.getBaseDirFiles(testBaseSrcDir, spec.argfiles); + final File[] injarFiles = + FileUtil.getBaseDirFiles(testBaseSrcDir, injarPaths); + final File[] aspectFiles = + FileUtil.getBaseDirFiles(testBaseSrcDir, spec.aspectpath); + final File[] classFiles = + FileUtil.getBaseDirFiles(testBaseSrcDir, spec.classpath); // hmm - duplicates validation above, verifying getBaseDirFiles? if (!spec.badInput) { if (!validator.canRead(argFiles, "argFiles") @@ -203,34 +215,53 @@ public class CompilerRun implements IAjcRun { final File[] srcFiles; File[] sourcerootFiles = new File[0]; // source text files are copied when staging incremental tests - if (!spec.isStaging()) { // XXX why this? was always? || (testBaseSrcDir != sandbox.stagingDir))) { - srcFiles = FileUtil.getBaseDirFiles(testBaseSrcDir, srcPaths, CompilerRun.SOURCE_SUFFIXES); + if (!spec.isStaging()) { + // XXX why this? was always? || (testBaseSrcDir != sandbox.stagingDir))) { + srcFiles = + FileUtil.getBaseDirFiles( + testBaseSrcDir, + srcPaths, + CompilerRun.SOURCE_SUFFIXES); if (!LangUtil.isEmpty(spec.sourceroots)) { - sourcerootFiles = FileUtil.getBaseDirFiles(testBaseSrcDir, spec.sourceroots, null); + sourcerootFiles = + FileUtil.getBaseDirFiles( + testBaseSrcDir, + spec.sourceroots, + null); } } else { // staging - copy files if (spec.badInput) { - validator.info("badInput ignored - files checked when staging"); + validator.info( + "badInput ignored - files checked when staging"); } try { // copy all files, then remove tagged ones // XXX make copyFiles support a filter? - srcFiles = FileUtil.copyFiles(testBaseSrcDir, srcPaths, sandbox.stagingDir); + srcFiles = + FileUtil.copyFiles( + testBaseSrcDir, + srcPaths, + sandbox.stagingDir); if (!LangUtil.isEmpty(spec.sourceroots)) { - sourcerootFiles = FileUtil.copyFiles(testBaseSrcDir, spec.sourceroots, sandbox.stagingDir); + sourcerootFiles = + FileUtil.copyFiles( + testBaseSrcDir, + spec.sourceroots, + sandbox.stagingDir); // delete incremental files in sourceroot after copying // XXX inefficient FileFilter pickIncFiles = new FileFilter() { - // an incremental file has an extra "." in name - // most .java files don't, because they are named after - // the principle type they contain, and simple type names - // have no dots. - public boolean accept(File file) { - if (file.isDirectory()) { // continue recursion + // an incremental file has an extra "." in name + // most .java files don't, because they are named after + // the principle type they contain, and simple type names + // have no dots. + public boolean accept(File file) { + if (file.isDirectory()) { + // continue recursion return true; } String path = file.getPath(); // only source files are relevant to staging - if (!FileUtil.hasSourceSuffix(path)) { + if (!FileUtil.hasSourceSuffix(path)) { return false; } int first = path.indexOf("."); @@ -239,13 +270,18 @@ public class CompilerRun implements IAjcRun { } }; for (int i = 0; i < sourcerootFiles.length; i++) { - FileUtil.deleteContents(sourcerootFiles[i], pickIncFiles, false); + FileUtil.deleteContents( + sourcerootFiles[i], + pickIncFiles, + false); } if (0 < sourcerootFiles.length) { - FileUtil.sleepPastFinalModifiedTime(sourcerootFiles); + FileUtil.sleepPastFinalModifiedTime( + sourcerootFiles); } } - File[] files = FileUtil.getBaseDirFiles(sandbox.stagingDir, srcPaths); + File[] files = + FileUtil.getBaseDirFiles(sandbox.stagingDir, srcPaths); if (0 < files.length) { FileUtil.sleepPastFinalModifiedTime(files); } @@ -257,7 +293,8 @@ public class CompilerRun implements IAjcRun { return false; } } - if (!spec.badInput && !validator.canRead(srcFiles, "copied paths")) { + if (!spec.badInput + && !validator.canRead(srcFiles, "copied paths")) { return false; } arguments.clear(); @@ -279,12 +316,13 @@ public class CompilerRun implements IAjcRun { arguments.add("@" + ra[j]); } if (!spec.badInput && spec.isStaging) { - validator.fail("warning: files listed in argfiles not staged"); - } + validator.fail( + "warning: files listed in argfiles not staged"); + } } // save classpath and aspectpath in sandbox for this and other clients - final boolean checkReadable = !spec.badInput; + final boolean checkReadable = !spec.badInput; int size = spec.includeClassesDir ? 3 : 2; File[] cp = new File[size + classFiles.length]; System.arraycopy(classFiles, 0, cp, 0, classFiles.length); @@ -299,7 +337,7 @@ public class CompilerRun implements IAjcRun { if (0 < aspectFiles.length) { sandbox.setAspectpath(aspectFiles, checkReadable, this); } - + // set bootclasspath if set for forking AbstractRunSpec.Fork fork = spec.getFork(); String bootclasspath = fork.getJavaBootclasspath(); @@ -308,7 +346,7 @@ public class CompilerRun implements IAjcRun { } return true; } - + /** * Setup result evaluation and command line, run, and evaluate result. * <li>setup an AjcMessageHandler using the expected messages from @@ -325,20 +363,23 @@ public class CompilerRun implements IAjcRun { * <li>After running, report AjcMessageHandler results to the status parameter. * If the AjcMessageHandler reports a failure, then send info messages * for the Spec, TestSetup, and command line.<li> - * @see org.aspectj.testing.run.IRun#run(IRunStatus) - */ - public boolean run(IRunStatus status) { + * @see org.aspectj.testing.run.IRun#run(IRunStatus) + */ + public boolean run(IRunStatus status) { if (null == spec.testSetup) { - MessageUtil.abort(status, "no test setup - adoptParentValues not called"); + MessageUtil.abort( + status, + "no test setup - adoptParentValues not called"); return false; } else if (!spec.testSetup.result) { MessageUtil.abort(status, spec.testSetup.failureReason); return false; } - boolean ignoreWarnings = (spec.testSetup.ignoreWarningsSet + boolean ignoreWarnings = + (spec.testSetup.ignoreWarningsSet && spec.testSetup.ignoreWarnings); - AjcMessageHandler handler - = new AjcMessageHandler(spec.getMessages()); + AjcMessageHandler handler = + new AjcMessageHandler(spec.getMessages()); handler.init(); boolean handlerResult = false; boolean result = false; @@ -349,9 +390,12 @@ public class CompilerRun implements IAjcRun { argList.add("-d"); String outputDirPath = sandbox.classesDir.getAbsolutePath(); try { // worth it to try for canonical? - outputDirPath = sandbox.classesDir.getCanonicalPath(); + outputDirPath = sandbox.classesDir.getCanonicalPath(); } catch (IOException e) { - MessageUtil.abort(status, "canonical " + sandbox.classesDir, e); + MessageUtil.abort( + status, + "canonical " + sandbox.classesDir, + e); } argList.add(outputDirPath); @@ -365,7 +409,7 @@ public class CompilerRun implements IAjcRun { argList.add("-bootclasspath"); argList.add(path); } - + path = sandbox.aspectpathToString(this); if (!LangUtil.isEmpty(path)) { argList.add("-aspectpath"); @@ -374,54 +418,81 @@ public class CompilerRun implements IAjcRun { if (0 < injars.size()) { argList.add("-injars"); - argList.add(FileUtil.flatten((String[]) injars.toArray(new String[0]), null)); + argList.add( + FileUtil.flatten( + (String[]) injars.toArray(new String[0]), + null)); } - + // put specified arguments last, for better badInput tests argList.addAll(setupResult.commandOptions); // add both java/aspectj and argfiles argList.addAll(arguments); - + // XXX hack - seek on request as a side effect. reimplement as listener if (null != setupResult.seek) { String slopPrefix = Spec.SEEK_MESSAGE_PREFIX + " slop - "; - PrintStream slop = MessageUtil.handlerPrintStream( - status, - IMessage.INFO, - System.err, - slopPrefix); - List found = FileUtil.lineSeek(setupResult.seek, arguments, false, slop); + PrintStream slop = + MessageUtil.handlerPrintStream( + status, + IMessage.INFO, + System.err, + slopPrefix); + List found = + FileUtil.lineSeek( + setupResult.seek, + arguments, + false, + slop); if (!LangUtil.isEmpty(found)) { - for (Iterator iter = found.iterator(); iter.hasNext();) { - MessageUtil.info(status, Spec.SEEK_MESSAGE_PREFIX + iter.next()); - } + for (Iterator iter = found.iterator(); + iter.hasNext(); + ) { + MessageUtil.info( + status, + Spec.SEEK_MESSAGE_PREFIX + iter.next()); + } } } - ICommand compiler = spec.reuseCompiler - ? sandbox.getCommand(this) // throws IllegalStateException if null - : ReflectionFactory.makeCommand(setupResult.compilerName, status); + ICommand compiler = spec.reuseCompiler + // throws IllegalStateException if null + ? sandbox.getCommand(this) + : ReflectionFactory.makeCommand(setupResult.compilerName, status); DirChanges dirChanges = null; if (null == compiler) { - MessageUtil.fail(status, "unable to make compiler " + setupResult.compilerName); + MessageUtil.fail( + status, + "unable to make compiler " + setupResult.compilerName); return false; } else { if (setupResult.compilerName != Spec.DEFAULT_COMPILER) { - MessageUtil.info(status, "compiler: " + setupResult.compilerName); + MessageUtil.info( + status, + "compiler: " + setupResult.compilerName); } if (status.aborted()) { - MessageUtil.debug(status, "aborted, but compiler valid?: " + compiler); + MessageUtil.debug( + status, + "aborted, but compiler valid?: " + compiler); } else { // same DirChanges handling for JavaRun, CompilerRun, IncCompilerRun // XXX around advice or template method/class if (!LangUtil.isEmpty(spec.dirChanges)) { - LangUtil.throwIaxIfFalse(1 == spec.dirChanges.size(), "expecting 0..1 dirChanges"); - dirChanges = new DirChanges((DirChanges.Spec) spec.dirChanges.get(0)); - if (!dirChanges.start(status, sandbox.classesDir)) { + LangUtil.throwIaxIfFalse( + 1 == spec.dirChanges.size(), + "expecting 0..1 dirChanges"); + dirChanges = + new DirChanges( + (DirChanges.Spec) spec.dirChanges.get(0)); + if (!dirChanges + .start(status, sandbox.classesDir)) { return false; // setup failed } } - MessageUtil.info(status, compiler + "(" + argList + ")"); + MessageUtil.info( + status, + compiler + "(" + argList + ")"); sandbox.setCommand(compiler, this); String[] args = (String[]) argList.toArray(RA_String); commandResult = compiler.runCommand(args, handler); @@ -431,11 +502,12 @@ public class CompilerRun implements IAjcRun { if (!handlerResult) { return false; } else { - result = (commandResult == handler.expectingCommandTrue()); - if (! result) { - String m = commandResult - ? "compile did not fail as expected" - : "compile failed unexpectedly"; + result = (commandResult == handler.expectingCommandTrue()); + if (!result) { + String m = + commandResult + ? "compile did not fail as expected" + : "compile failed unexpectedly"; MessageUtil.fail(status, m); } else if (null != dirChanges) { result = dirChanges.end(status, sandbox.testBaseDir); @@ -450,111 +522,163 @@ public class CompilerRun implements IAjcRun { MessageUtil.info(handler, "" + setupResult); } } - handler.report(status); // XXX weak - actual messages not reported in real-time, no fast-fail - } + handler.report(status); + // XXX weak - actual messages not reported in real-time, no fast-fail + } } - public String toString() { return "CompilerRun(" + spec + ")"; } - + /** * Initializer/factory for CompilerRun * any path or file is relative to this test base dir */ public static class Spec extends AbstractRunSpec { public static final String XMLNAME = "compile"; + public static final String DEFAULT_COMPILER = + ReflectionFactory.ECLIPSE; static final String SEEK_PREFIX = "-seek:"; static final String SEEK_MESSAGE_PREFIX = "found: "; - - /** can't specify these as options */ - private static final String[] INVALID_OPTIONS = new String[] - { "-workingdir", "-argfile", "-sourceroots", "-outjar"}; - // when updating these, update tests/harness/selectionTest.xml - - - /** no support in the javac for these otherwise-valid options */ - private static final String[] INVALID_JAVAC_OPTIONS = new String[] - { "-lenient", "-strict", "-usejavac", "-preprocess", - "-XOcodeSize", "-XSerializable", "-XaddSafePrefix", - "-XtargetNearSource", - "-incremental", "-Xlint", "-aspectpath", - "workingdir", "-argfile", "-sourceroots", "-outjar", - }; - - /** no support in the eclipse-based compiler for these otherwise-valid options */ - private static final String[] INVALID_ECLIPSE_OPTIONS = new String[] - { "-lenient", "-strict", "-usejavac", "-preprocess", - "-XOcodeSize", "-XSerializable", "-XaddSafePrefix", - "-XtargetNearSource" }; - - /** options supported by the harness */ - private static final String[] VALID_OPTIONS = new String[] - { - SEEK_PREFIX, - // eajc does not support -usejavac, -preprocess - // testFlag() handles -ajc, -eclipse, -javac, -ajdeCompiler, -ignoreWarnings - "-usejavac", "-preprocess", - "-Xlint", "-lenient", "-strict", - "-source14", "-verbose", "-emacssym", - "-ajc", "-eclipse", "-ajdeCompiler", "-ajctaskCompiler", "-javac", - "-ignoreWarnings", "-1.3", "-1.4", "-1.5", - // XXX consider adding [!^]ajdeCompiler - "!usejavac", "!preprocess", - "!Xlint", "!lenient", "!strict", - "!source14", "!verbose", "!emacssym", - "!ajc", "!eclipse", - - "^usejavac", "^preprocess", - "^Xlint", "^lenient", "^strict", - "^source14", "^verbose", "^emacssym", - "^ajc", "^eclipse" - }; - public static final String DEFAULT_COMPILER - = ReflectionFactory.ECLIPSE; + + private static final CRSOptions CRSOPTIONS = new CRSOptions(); + /** + * Retitle description to title, paths to files, do comment, + * staging, badInput, + * do dirChanges, and print no chidren. + */ + private static final AbstractRunSpec.XMLNames NAMES = + new AbstractRunSpec.XMLNames( + AbstractRunSpec.XMLNames.DEFAULT, + "title", + null, + null, + null, + "files", + null, + null, + null, + false, + false, + true); /** - * Retitle description to title, paths to files, do comment, - * staging, badInput, - * do dirChanges, and print no chidren. + * If the source version warrants, add a -bootclasspath + * entry to the list of arguments to add. + * This will fail and return an error String if the + * required library is not found. + * @param sourceVersion the String (if any) describing the -source option + * (expecting one of [null, "1.3", "1.4", "1.5"]. + * @param compilerName the String name of the target compiler + * @param toAdd the ArrayList to add -bootclasspath to + * @return the String describing any errors, or null if no errors */ - private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT, - "title", null, null, null, "files", null, null, null, false, false, true); - + private static String updateBootclasspathForSourceVersion( + String sourceVersion, + String compilerName, + ArrayList toAdd) { + if (null == sourceVersion) { + return null; + } + if (3 != sourceVersion.length()) { + throw new IllegalArgumentException( + "bad version: " + sourceVersion); + } + if (null == toAdd) { + throw new IllegalArgumentException("null toAdd"); + } + int version = sourceVersion.charAt(2) - '0'; + switch (version) { + case (3) : + if (LangUtil.supportsJava("1.4")) { + if (!FileUtil.canReadFile(Globals.J2SE13_RTJAR)) { + return "no 1.3 libraries to handle -source 1.3"; + } + toAdd.add("-bootclasspath"); + toAdd.add(Globals.J2SE13_RTJAR.getAbsolutePath()); + } + break; + case (4) : + if (!LangUtil.supportsJava("1.4")) { + if (ReflectionFactory + .ECLIPSE + .equals(compilerName)) { + return "run eclipse under 1.4 to handle -source 1.4"; + } + if (!FileUtil.canReadFile(Globals.J2SE14_RTJAR)) { + return "no 1.4 libraries to handle -source 1.4"; + } + toAdd.add("-bootclasspath"); + toAdd.add(Globals.J2SE14_RTJAR.getAbsolutePath()); + } + break; + case (5) : + return "1.5 not supported in CompilerRun"; + case (0) : + // ignore - no version specified + break; + default : + throw new Error("unexpected version: " + version); + } + return null; + } + + static CRSOptions testAccessToCRSOptions() { + return CRSOPTIONS; + } + + static Options testAccessToOptions() { + return CRSOPTIONS.getOptions(); + } + + private static String[] copy(String[] input) { + if (null == input) { + return null; + } + String[] result = new String[input.length]; + System.arraycopy(input, 0, result, 0, input.length); + return result; + } + + /** @return true if javac is available on the classpath */ + private static boolean haveJavac() { // XXX copy/paste from JavaCWrapper.java + Class compilerClass = null; + try { + compilerClass = Class.forName("com.sun.tools.javac.Main"); + } catch (ClassNotFoundException ce1) { + try { + compilerClass = Class.forName("sun.tools.javac.Main"); + } catch (ClassNotFoundException ce2) { + } + } + return (null != compilerClass); + } + protected String compiler; - + // use same command - see also IncCompiler.Spec.fresh protected boolean reuseCompiler; protected boolean includeClassesDir; - + protected TestSetup testSetup; - + protected String[] argfiles = new String[0]; protected String[] aspectpath = new String[0]; protected String[] classpath = new String[0]; // XXX unused protected String[] sourceroots = new String[0]; - + /** src path = {suiteParentDir}/{testBaseDirOffset}/{testSrcDirOffset}/{path} */ protected String testSrcDirOffset; - + public Spec() { super(XMLNAME); setXMLNames(NAMES); compiler = DEFAULT_COMPILER; } - - private static String[] copy(String[] input) { - if (null == input) { - return null; - } - String[] result = new String[input.length]; - System.arraycopy(input, 0, result, 0, input.length); - return result; - } - protected void initClone(Spec spec) - throws CloneNotSupportedException { + protected void initClone(Spec spec) + throws CloneNotSupportedException { super.initClone(spec); spec.argfiles = copy(argfiles); spec.aspectpath = copy(aspectpath); @@ -569,11 +693,11 @@ public class CompilerRun implements IAjcRun { } spec.testSrcDirOffset = testSrcDirOffset; } - + public Object clone() throws CloneNotSupportedException { Spec result = new Spec(); initClone(result); - return result; + return result; } public void setIncludeClassesDir(boolean include) { @@ -582,17 +706,17 @@ public class CompilerRun implements IAjcRun { public void setReuseCompiler(boolean reuse) { this.reuseCompiler = reuse; } - + public void setCompiler(String compilerName) { this.compiler = compilerName; - } + } public void setTestSrcDirOffset(String s) { if (null != s) { testSrcDirOffset = s; } } - + /** override to set dirToken to Sandbox.CLASSES and default suffix to ".class" */ public void addDirChanges(DirChanges.Spec spec) { if (null == spec) { @@ -602,7 +726,7 @@ public class CompilerRun implements IAjcRun { spec.setDefaultSuffix(".class"); super.addDirChanges(spec); } - + public String toLongString() { return getPrintName() + "(" + super.containedSummary() + ")"; } @@ -611,11 +735,11 @@ public class CompilerRun implements IAjcRun { return getPrintName() + "(" + super.containedSummary() + ")"; } - /** bean mapping for writers */ + /** bean mapping for writers */ public void setFiles(String paths) { addPaths(paths); } - + /** * Add to default classpath * (which includes aspectjrt.jar and testing-client.jar). @@ -632,7 +756,7 @@ public class CompilerRun implements IAjcRun { * Set source roots, deleting any old ones * @param files comma-delimited list of directories * - ignored if null or empty - */ + */ public void setSourceroots(String dirs) { if (!LangUtil.isEmpty(dirs)) { sourceroots = XMLWriter.unflattenList(dirs); @@ -643,7 +767,7 @@ public class CompilerRun implements IAjcRun { * Set aspectpath, deleting any old ones * @param files comma-delimited list of aspect jars - ignored if null or * empty - */ + */ public void setAspectpath(String files) { if (!LangUtil.isEmpty(files)) { aspectpath = XMLWriter.unflattenList(files); @@ -653,7 +777,7 @@ public class CompilerRun implements IAjcRun { /** * Set argfiles, deleting any old ones * @param files comma-delimited list of argfiles - ignored if null or empty - */ + */ public void setArgfiles(String files) { if (!LangUtil.isEmpty(files)) { argfiles = XMLWriter.unflattenList(files); @@ -668,20 +792,22 @@ public class CompilerRun implements IAjcRun { } return (String[]) LangUtil.copy(argfiles); } - + /** * This implementation skips if: * <ul> * <li>incremental test, but using ajc (not eclipse)</li> * <li>usejavac, but javac is not available on the classpath</li> * <li>eclipse, but -usejavac or -preprocess test</li> - * <li>-source14, but running under 1.2 (XXX design)</li> + * <li>-source 1.4, but running under 1.2 (XXX design)</li> * <li>local/global option conflicts (-lenient/-strict)</li> * <li>semantic conflicts (e.g., -lenient/-strict)</li> * </ul> * @return false if this wants to be skipped, true otherwise */ - protected boolean doAdoptParentValues(RT parentRuntime, IMessageHandler handler) { + protected boolean doAdoptParentValues( + RT parentRuntime, + IMessageHandler handler) { if (!super.doAdoptParentValues(parentRuntime, handler)) { return false; } @@ -690,33 +816,35 @@ public class CompilerRun implements IAjcRun { skipMessage(handler, testSetup.failureReason); } return testSetup.result; - } - + } - private String getShortCompilerName() { - String cname = compiler; + private String getShortCompilerName() { + String compilerClassName = compiler; if (null != testSetup) { - cname = testSetup.compilerName; - } - if (null != cname) { - int loc = cname.lastIndexOf("."); - if (-1 != loc) { - cname = cname.substring(loc+1); - } - } - return cname; - } - + compilerClassName = testSetup.compilerName; + } + if (null != compilerClassName) { + int loc = compilerClassName.lastIndexOf("."); + if (-1 != loc) { + compilerClassName = + compilerClassName.substring(loc + 1); + } + } + return compilerClassName; + } + /** @return a CompilerRun with this as spec if setup completes successfully. */ - public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator) { - CompilerRun run = new CompilerRun(this); + public IRunIterator makeRunIterator( + Sandbox sandbox, + Validator validator) { + CompilerRun run = new CompilerRun(this); if (run.setupAjcRun(sandbox, validator)) { // XXX need name for compilerRun return new WrappedRunIterator(this, run); } return null; - } - + } + protected String getPrintName() { return "CompilerRun.Spec " + getShortCompilerName(); } @@ -750,9 +878,6 @@ public class CompilerRun implements IAjcRun { * <li>lenient, strict</li> * </ul> * <p> - * The -source 1.4 flag should always be specified as -source14, - * as this will otherwise fail to process it correctly. - * This converts it back to -source 1.4. * <p> * This also interprets any relevant System properties, * e.g., from <code>JavaRun.BOOTCLASSPATH_KEY</code>. @@ -773,298 +898,346 @@ public class CompilerRun implements IAjcRun { * and commandOptions is not null, and any test flags (ignore warning, * compiler) are reflected in the TestSetup. * If this fails, then TestSetup.result is false, - * and a TestSetup.failreason is set. + * and a TestSetup.failreason is set. + * This means the test is skipped. * @return TestSetup with results * (TestSetup result=false if the run should not continue) */ protected TestSetup setupArgs(IMessageHandler handler) { // warning: HarnessSelectionTest checks for specific error wording - ArrayList argList = new ArrayList(); - argList.addAll(getOptionsList()); final Spec spec = this; - TestSetup result = new TestSetup(); - if (argList.contains("-source")) { - result.failureReason = "use -source14 for -source 1.4: " + argList; - return result; - } + final TestSetup result = new TestSetup(); result.compilerName = spec.compiler; - String[] globalOptions = spec.runtime.extractOptions(Spec.VALID_OPTIONS, true); - if ((null != globalOptions) && (globalOptions.length > 0)) { - // --- fold in globals, removing conflicts, etc. - for (int i = 0; i < globalOptions.length; i++) { - String globalArg = globalOptions[i]; - if ((null == globalArg) || (2 > globalArg.length())) { - continue; - } else if (globalArg.startsWith(SEEK_PREFIX)) { - result.seek = globalArg.substring(SEEK_PREFIX.length()); - continue; - } else if ("-source".equals(globalArg)) { - result.failureReason = "use -source14 for -source 1.4 [" + i + "]"; - return result; - } - char first = globalArg.charAt(0); - globalArg = globalArg.substring(1); - boolean globalForceOn = (first == '!'); - boolean globalForceOff = (first == '^'); - boolean globalSet = (first == '-'); - if (!globalSet && !globalForceOn && !globalForceOff) { - MessageUtil.info(handler, "ignoring bad global: " + globalOptions[i]); - continue; - } - int argIndex = indexOf(globalArg, argList); - if (-1 == argIndex) { // no apparent conflict - look for eclipse/ajc conflicts XXX unresolved - boolean ajcGlobal = true; - if ("ajc".equals(globalArg)) { - argIndex = indexOf("eclipse", argList); - } else if ("eclipse".equals(globalArg)) { - argIndex = indexOf("ajc", argList); - ajcGlobal = false; - } - if (-1 != argIndex) { // resolve eclipse/ajc conflict - String arg = ((String) argList.get(argIndex)); - char argFirst = arg.charAt(0); - argList.remove(arg); // replace with resolved variant... - char ajcFirst; - char eclipseFirst; - if (ajcGlobal) { - ajcFirst = first; - eclipseFirst = argFirst; - } else { - ajcFirst = argFirst; - eclipseFirst = first; - } - if ('!' == eclipseFirst) { - if ('!' == ajcFirst) { - result.failureReason = "conflict between !eclipse and !ajc"; - return result; - } else { - argList.add("-eclipse"); - } - } else if (('!' == ajcFirst) || ('^' == eclipseFirst)) { - argList.add("-ajc"); - } else if ('^' == ajcFirst) { - argList.add("-eclipse"); - } else if (('-' != ajcFirst) || ('-' != eclipseFirst)) { - result.failureReason = "harness logic error resolving " - + arg + " and global " + globalArg; - return result; - } else if (ajcGlobal) { - argList.add("-ajc"); - } else { - argList.add("-eclipse"); - } - continue; // resolved - } - } - - if (-1 == argIndex) { // no dup, so no conflict - if (!globalForceOff) { - argList.add("-" + globalArg); - } - } else { // have conflict - resolve - String arg = (String) argList.get(argIndex); - first = arg.charAt(0); - boolean localForceOn = (first == '!'); - boolean localForceOff = (first == '^'); - boolean localSet = (first == '-'); - if (!localSet && !localForceOn && !localForceOff) { - result.failureReason = "only handling [-^!]{arg}: " + arg; - return result; - } - if ((localForceOn && globalForceOff) - || (localForceOff && globalForceOn)) { - result.failureReason = "force conflict between arg=" - + arg + " and global=" + globalOptions[i]; - return result; - } - if (globalForceOn) { - if (localForceOn) { // localSet is already correct, localForceOff was conflict - argList.remove(arg); // no !funkiness - argList.add("-" + globalArg); - } - } else if (globalSet) { - if (localSet) { - // do nothing - already correct - } else if (localForceOn) { - argList.remove(arg); // no !funkiness - argList.add("-" + globalArg); - } - } else if (globalForceOff) { - argList.remove(arg); - } else { - throw new Error("illegal arg state?? : " + arg); - //MessageUtil.info(handler, "illegal arg state??: " + arg); - } - } - } + // urk - s.b. null, but expected + Values values = gatherValues(result); + + if ((null == values) || (null != result.failureReason)) { + return checkResult(result); } + // send info messages about // forced staging when -incremental // or staging but no -incremental flag - int incLoc = argList.indexOf("-incremental"); + Option.Family getFamily = + CRSOPTIONS.crsIncrementalOption.getFamily(); + final boolean haveIncrementalFlag = + (null != values.firstInFamily(getFamily)); + if (spec.isStaging()) { - if (-1 == incLoc) { // staging and no flag - MessageUtil.info(handler, "staging but no -incremental flag"); + if (!haveIncrementalFlag) { + MessageUtil.info( + handler, + "staging but no -incremental flag"); } - } else if (-1 != incLoc) { // flagged but not staging - stage + } else if (haveIncrementalFlag) { spec.setStaging(true); MessageUtil.info(handler, "-incremental forcing staging"); } - // remove funky prefixes from remainder, fixup two-part flags - // and interpret special flags - boolean source14 = false; - ArrayList toAdd = new ArrayList(); - for (ListIterator iter = argList.listIterator(); iter.hasNext();) { - String arg = (String) iter.next(); - if (testFlag(arg, result)) { - iter.remove(); - continue; - } - char c = arg.charAt(0); - String rest = arg.substring(1); - if (c == '^') { - iter.remove(); - continue; - } - if (c == '!') { - iter.remove(); - if (!("source14".equals(rest))) { - toAdd.add("-" + rest); - } else { - source14 = true; - } - rest = null; - } else if ("source14".equals(rest)) { - iter.remove(); - source14 = true; - } + + if (hasInvalidOptions(values, result)) { + return checkResult(result); } - if (source14) { - // must run under 1.4 VM or (if ajc) set up bootclasspath - if (!LangUtil.supportsJava("1.4")) { - if (ReflectionFactory.ECLIPSE.equals(result.compilerName)) { - result.failureReason - = "eclipse must run under 1.4 to implement -source 1.4"; - return result; - } - - if (!FileUtil.canReadFile(Globals.J2SE14_RTJAR)) { - result.failureReason - = "unable to get 1.4 libraries to implement -source 1.4"; - return result; - } - toAdd.add("-bootclasspath"); - toAdd.add(Globals.J2SE14_RTJAR.getAbsolutePath()); - } - toAdd.add("-source"); - toAdd.add("1.4"); - } - argList.addAll(toAdd); - - // finally, check for semantic conflicts - String[] badOptions = LangUtil.selectOptions(argList, Spec.INVALID_OPTIONS); - if (!LangUtil.isEmpty(badOptions)) { - result.failureReason = "no support for (normally-valid) options " - + Arrays.asList(badOptions); - } else if (argList.contains("-lenient") && argList.contains("-strict")) { - result.failureReason = "semantic conflict -lenient | -strict"; - } else if (ReflectionFactory.OLD_AJC.equals(result.compilerName)) { - if (spec.isStaging) { - result.failureReason = "OLD_AJC does not do incremental compiles"; - } else if (argList.contains("-usejavac") && !haveJavac()) { - result.failureReason = "-usejavac but no javac on classpath"; - } else { - result.result = true; - } - } else if (ReflectionFactory.ECLIPSE.equals(result.compilerName) - || AJDE_COMPILER.equals(result.compilerName) - || AJCTASK_COMPILER.equals(result.compilerName) - || BUILDER_COMPILER.equals(result.compilerName)) { - badOptions = LangUtil.selectOptions(argList, Spec.INVALID_ECLIPSE_OPTIONS); - if (!LangUtil.isEmpty(badOptions)) { - result.failureReason = "no support in eclipse-based compiler" - + " for (normally-valid) options "+ Arrays.asList(badOptions); - } else { - result.result = true; + + // set compiler in result + getFamily = CRSOPTIONS.ajccompilerOption.getFamily(); + Option.Value compiler = values.firstInFamily(getFamily); + if (null != compiler) { + result.compilerName + = CRSOPTIONS.compilerClassName(compiler.option); + if (null == result.compilerName) { + result.failureReason = + "unable to get class name for " + compiler; + return checkResult(result); } -// } else if (JAVAC_COMPILER.equals(result.compilerName)) { -// // XXX vet -// badOptions = LangUtil.selectOptions(argList, Spec.INVALID_JAVAC_OPTIONS); -// if (!LangUtil.isEmpty(badOptions)) { -// result.failureReason = "no support in javac" -// + " for (normally-valid) options " + Arrays.asList(badOptions); -// } else { -// result.result = true; -// } - } else { - result.failureReason = "unrecognized compiler: " + result.compilerName; } - if (result.result) { - result.commandOptions = argList; + String compilerName = + (null == result.compilerName + ? spec.compiler + : result.compilerName); + // check compiler semantics + if (hasCompilerSpecErrors(compilerName, values, result)) { + return checkResult(result); + } + // add toadd and finish result + ArrayList args = new ArrayList(); + String[] rendered = values.render(); + if (!LangUtil.isEmpty(rendered)) { + args.addAll(Arrays.asList(rendered)); } - return result; + // update bootclasspath + getFamily = CRSOPTIONS.crsSourceOption.getFamily(); + Option.Value source = values.firstInFamily(getFamily); + if (null != source) { + String sourceVersion = source.unflatten()[1]; + ArrayList toAdd = new ArrayList(); + String err = + updateBootclasspathForSourceVersion( + sourceVersion, + spec.compiler, + toAdd); + args.addAll(toAdd); + } + result.commandOptions = args; + result.result = true; + return checkResult(result); } - /** @return true if javac is available on the classpath */ - private boolean haveJavac() { // XXX copy/paste from JavaCWrapper.java - Class compilerClass = null; - try { - compilerClass = Class.forName("com.sun.tools.javac.Main"); - } catch (ClassNotFoundException ce1) { - try { - compilerClass = Class.forName("sun.tools.javac.Main"); - } catch (ClassNotFoundException ce2) { + /** + * Ensure exit invariant: + * <code>result.result == (null == result.failureReason) + * == (null != result.commandOptions)</code> + * @param result the TestSetup to verify + * @return result + * @throws Error if invariant is not true + */ + TestSetup checkResult(TestSetup result) { + String err = null; + if (null == result) { + err = "null result"; + } else if (result.result != (null == result.failureReason)) { + err = + result.result + ? "expected no failure: " + result.failureReason + : "fail for no reason"; + } else if (result.result != (null != result.commandOptions)) { + err = + result.result + ? "expected command options" + : "unexpected command options"; + } + if (null != err) { + throw new Error(err); + } + return result; + } + + boolean hasInvalidOptions(Values values, TestSetup result) { + // not supporting 1.0 options any more + for (Iterator iter = CRSOPTIONS.invalidOptions.iterator(); + iter.hasNext(); + ) { + Option option = (Option) iter.next(); + if (null != values.firstOption(option)) { + result.failureReason = + "invalid option in harness: " + option; + return true; } } - return (null != compilerClass); - } + return false; + } - - /** - * Handle flags that are interpreted by the test rather than the - * underlying command. These flags are to be removed from the - * arg list. - * @return true if this is a flag to remove from the arg list - */ - protected boolean testFlag(String arg, TestSetup result) { - if ("-ajdeCompiler".equals(arg)) { - result.compilerName = AJDE_COMPILER; - return true; - } else if ("-builderCompiler".equals(arg)) { - result.compilerName = BUILDER_COMPILER; - return true; - } else if ("-ajctaskCompiler".equals(arg)) { - result.compilerName = AJCTASK_COMPILER; - return true; -// } else if ("-javac".equals(arg)) { -// result.compilerName = JAVAC_COMPILER; -// return true; - } else if ("-eclipse".equals(arg) || "!eclipse".equals(arg) || "^ajc".equals(arg)) { - result.compilerName = ReflectionFactory.ECLIPSE; - return true; - } else if ("-ajc".equals(arg) || "!ajc".equals(arg) || "^eclipse".equals(arg)) { - result.compilerName = ReflectionFactory.OLD_AJC; + boolean hasCompilerSpecErrors( + String compilerName, + Values values, + TestSetup result) { + /* + * Describe any semantic conflicts between options. + * This skips: + * - old 1.0 options, including lenient v. strict + * - old ajc options, include !incremental and usejavac w/o javac + * - invalid eclipse options (mostly ajc) + * @param compilerName the String name of the target compiler + * @return a String describing any conflicts, or null if none + */ + if (!(ReflectionFactory.ECLIPSE.equals(compilerName) + || ReflectionFactory.OLD_AJC.equals(compilerName) + || CRSOPTIONS.AJDE_COMPILER.equals(compilerName) + || CRSOPTIONS.AJCTASK_COMPILER.equals(compilerName) + )) { + //|| BUILDER_COMPILER.equals(compilerName)) + result.failureReason = + "unrecognized compiler: " + compilerName; return true; - } else if ("-ignoreWarnings".equals(arg)) { - result.ignoreWarnings = true; - result.ignoreWarningsSet = true; + } + // not supporting ajc right now + if (null + != values.firstOption(CRSOPTIONS.ajccompilerOption)) { + result.failureReason = "ajc not supported"; return true; } + // not supporting 1.0 options any more + for (Iterator iter = CRSOPTIONS.ajc10Options.iterator(); + iter.hasNext(); + ) { + Option option = (Option) iter.next(); + if (null != values.firstOption(option)) { + result.failureReason = "old ajc 1.0 option: " + option; + return true; + } + } + return false; } - + + protected Values gatherValues(TestSetup result) { + final Spec spec = this; + // ---- local option values + final Values localValues; + final Options options = CRSOPTIONS.getOptions(); + try { + String[] input = getOptionsArray(); + // this handles reading options, + // flattening two-String options, etc. + localValues = options.acceptInput(input); + // all local values should be picked up + String err = Options.missedMatchError(input, localValues); + if (!LangUtil.isEmpty(err)) { + result.failureReason = err; + return null; + } + } catch (InvalidInputException e) { + result.failureReason = e.getFullMessage(); + return null; + } + + // ---- global option values + StringBuffer errs = new StringBuffer(); + final Values globalValues = + spec.runtime.extractOptions(options, true, errs); + if (errs.length() > 0) { + result.failureReason = errs.toString(); + return null; + } + final Values combined = + Values.wrapValues( + new Values[] { localValues, globalValues }); + + String err = combined.resolve(); + if (null != err) { + result.failureReason = err; + return null; + } + + return handleTestArgs(combined, result); + } + + // final int len = globalValues.length() + localValues.length(); + // final Option.Value[] combinedValues = new Option.Value[len]; + // System.arraycopy( + // globalValues, + // 0, + // combinedValues, + // 0, + // globalValues.length()); + // System.arraycopy( + // localValues, + // 0, + // combinedValues, + // globalValues.length(), + // localValues.length()); + // + // result.compilerName = spec.compiler; + // if (0 < combinedValues.length) { + // // this handles option forcing, etc. + // String err = Options.resolve(combinedValues); + // if (null != err) { + // result.failureReason = err; + // return null; + // } + // if (!handleTestArgs(combinedValues, result)) { + // return null; + // } + // } + // return Values.wrapValues(combinedValues); + // } + /** + * This interprets and nullifies values for the test. + * @param values the Option.Value[] being processed + * @param result the TestSetup to modify + * @return false if error (caller should return), true otherwise + */ + Values handleTestArgs(Values values, final TestSetup result) { + final Option.Family compilerFamily = + CRSOPTIONS.ajccompilerOption.getFamily(); + Values.Selector selector = new Values.Selector() { + protected boolean accept(Option.Value value) { + if (null == value) { + return false; + } + Option option = value.option; + if (compilerFamily.sameFamily(option.getFamily())) { + if (value.prefix.isSet()) { + String compilerClass + = CRSOPTIONS.compilerClassName(option); + if (null == compilerClass) { + result.failureReason = + "unrecognized compiler: " + value; + throw Values.Selector.STOP; + } + if (!CRSOPTIONS.compilerIsLoadable(option)) { + result.failureReason = + "unable to load compiler: " + option; + throw Values.Selector.STOP; + } + result.compilerName = compilerClass; + } + return true; + } else if ( + CRSOPTIONS.crsIgnoreWarnings.sameOptionIdentifier( + option)) { + result.ignoreWarnings = value.prefix.isSet(); + result.ignoreWarningsSet = true; + return true; + } + return false; + } + }; + return values.nullify(selector); + } + + // /** + // * This interprets and nullifies values for the test. + // * @param values the Option.Value[] being processed + // * @param result the TestSetup to modify + // * @return false if error (caller should return), true otherwise + // */ + // boolean handleTestArgs(Option.Value[] values, TestSetup result) { + // if (!LangUtil.isEmpty(values)) { + // for (int i = 0; i < values.length; i++) { + // Option.Value value = values[i]; + // if (null == value) { + // continue; + // } + // Option option = value.option; + // if (option.sameOptionFamily(ECLIPSE_OPTION)) { + // if (!value.prefix.isSet()) { + // values[i] = null; + // continue; + // } + // String compilerClass = + // (String) COMPILER_OPTION_TO_CLASSNAME.get( + // option); + // if (null == compilerClass) { + // result.failureReason = + // "unrecognized compiler: " + value; + // return false; + // } + // result.compilerName = compilerClass; + // values[i] = null; + // } else if ( + // option.sameOptionFamily(crsIgnoreWarnings)) { + // result.ignoreWarnings = value.prefix.isSet(); + // result.ignoreWarningsSet = true; + // values[i] = null; + // } + // } + // } + // return true; + // } + // XXX need keys, cache... /** @return index of global in argList, ignoring first char */ protected int indexOf(String global, ArrayList argList) { int max = argList.size(); for (int i = 0; i < max; i++) { - if (global.equals(((String) argList.get(i)).substring(1))) { + if (global + .equals(((String) argList.get(i)).substring(1))) { return i; - } + } } return -1; } - + /** * Write this out as a compile element as defined in * AjcSpecXmlReader.DOCTYPE. @@ -1087,13 +1260,19 @@ public class CompilerRun implements IAjcRun { out.printAttribute("includeClassesDir", "true"); } if (!LangUtil.isEmpty(argfiles)) { - out.printAttribute("argfiles", XMLWriter.flattenFiles(argfiles)); + out.printAttribute( + "argfiles", + XMLWriter.flattenFiles(argfiles)); } if (!LangUtil.isEmpty(aspectpath)) { - out.printAttribute("aspectpath", XMLWriter.flattenFiles(argfiles)); + out.printAttribute( + "aspectpath", + XMLWriter.flattenFiles(argfiles)); } if (!LangUtil.isEmpty(sourceroots)) { - out.printAttribute("sourceroots", XMLWriter.flattenFiles(argfiles)); + out.printAttribute( + "sourceroots", + XMLWriter.flattenFiles(argfiles)); } out.endAttributes(); if (!LangUtil.isEmpty(dirChanges)) { @@ -1102,7 +1281,7 @@ public class CompilerRun implements IAjcRun { SoftMessage.writeXml(out, getMessages()); out.endElement(xmlElementName); } - + /** * Encapsulate the directives that can be set using * global arguments supplied in {@link Spec.getOptions()}. @@ -1116,22 +1295,22 @@ public class CompilerRun implements IAjcRun { * to ignore warnings in its result evaluation */ boolean ignoreWarningsSet; - + /** if telling AjcMessageHandler, what we tell it */ boolean ignoreWarnings; - + /** false if setup failed */ boolean result; - + /** if setup failed, this has the reason why */ String failureReason; - + /** beyond running test, also seek text in sources */ String seek; - + /** if setup completed, this has the combined global/local options */ ArrayList commandOptions; - + public Object clone() { TestSetup testSetup = new TestSetup(); testSetup.compilerName = compilerName; @@ -1149,13 +1328,326 @@ public class CompilerRun implements IAjcRun { public String toString() { return "TestSetup(" + (null == compilerName ? "" : compilerName + " ") - + (!ignoreWarningsSet ? "" + + (!ignoreWarningsSet + ? "" : (ignoreWarnings ? "" : "do not ") + "ignore warnings ") + (result ? "" : "setup failed") + ")"; } } + + /** + * Options-related stuff in the spec. + */ + static class CRSOptions { + // static final String BUILDER_COMPILER = + // "org.aspectj.ajdt.internal.core.builder.Builder.Command"; + static final String AJDE_COMPILER = + CompileCommand.class.getName(); + + static final String AJCTASK_COMPILER = + AjcTaskCompileCommand.class.getName(); + + private final Map compilerOptionToLoadable = new TreeMap(); + /* + * The options field in a compiler test permits some arbitrary + * command-line options to be set. It does not permit things + * like classpath, aspectpath, files, etc. which are set + * using other fields in the test specification, so the options + * permitted are a subset of those permitted on the command-line. + * + * Global options specified on the harness command-line are + * adopted for the compiler command-line if they are permitted + * in the options field. That means we have to detect each + * permitted option, rather than just letting all through + * for the compiler. + * + * Conversely, some options are targeted not at the compiler, + * but at the test itself (e.g., to ignore warnings, or to + * select a compiler. + * + * The harness can run many compilers, and they differ in + * which options are permitted. You can specify a compiler + * as an option (e.g., -eclipse). So the set of constraints + * on the list of permitted options can differ from test to test. + * + * The following code sets up the lists of permitted options + * and breaks out subsets for different compiler-variant checks. + * Most options are created but not named, but some options + * are named to detect corresponding values for further + * processing. e.g., the compiler options are saved so + * we can do per-compiler option verification. + * + */ + private final Options crsOptions; + private final Family compilerFamily; + private final Option crsIncrementalOption; + private final Option crsSourceOption; + // these are options handled/absorbed by CompilerRun + private final Option crsIgnoreWarnings; + private final Option eclipseOption; + private final Option buildercompilerOption; + private final Option ajdecompilerOption; + private final Option javacOption; + private final Option ajctaskcompilerOption; + private final Option ajccompilerOption; + private final Map compilerOptionToClassname; + private final Set compilerOptions; + // compiler verification - permit but flag ajc 1.0 options + private final List ajc10Options; + private final List invalidOptions; + + private CRSOptions() { + crsOptions = new Options(true); + Option.Factory factory = new Option.Factory("CompilerRun"); + // compiler options go in map + eclipseOption = + factory.create( + "eclipse", + "compiler", + Option.FORCE_PREFIXES, + false); + compilerFamily = eclipseOption.getFamily(); + buildercompilerOption = + factory.create( + "builderCompiler", + "compiler", + Option.FORCE_PREFIXES, + false); + ajctaskcompilerOption = + factory.create( + "ajctaskCompiler", + "compiler", + Option.FORCE_PREFIXES, + false); + ajdecompilerOption = + factory.create( + "ajdeCompiler", + "compiler", + Option.FORCE_PREFIXES, + false); + ajccompilerOption = + factory.create( + "ajc", + "compiler", + Option.FORCE_PREFIXES, + false); + javacOption = + factory.create( + "javac", + "compiler", + Option.FORCE_PREFIXES, + false); + + Map map = new TreeMap(); + map.put(eclipseOption, ReflectionFactory.ECLIPSE); + //map.put(BUILDERCOMPILER_OPTION, BUILDER_COMPILER); + map.put( + ajctaskcompilerOption, + AJCTASK_COMPILER); + map.put(ajdecompilerOption, AJDE_COMPILER); + map.put(ajccompilerOption, ReflectionFactory.OLD_AJC); + //map.put(JAVAC_OPTION, "XXX javac option not supported"); + compilerOptionToClassname = + Collections.unmodifiableMap(map); + + compilerOptions = + Collections.unmodifiableSet( + compilerOptionToClassname.keySet()); + // options not permitted in the harness + List list = new ArrayList(); + list.add(factory.create("workingdir")); + list.add(factory.create("argfile")); + list.add(factory.create("sourceroots")); + list.add(factory.create("outjar")); + invalidOptions = Collections.unmodifiableList(list); + + // other options added directly + crsIncrementalOption = factory.create("incremental"); + + crsIgnoreWarnings = factory.create("ignoreWarnings"); + + crsSourceOption = + factory + .create( + "source", + "source", + Option.FORCE_PREFIXES, + false, + new String[][] { new String[] { "1.3", "1.4" } + }); + + // ajc 1.0 options + // workingdir above in invalid options + list = new ArrayList(); + list.add(factory.create("usejavac")); + list.add(factory.create("preprocess")); + list.add(factory.create("nocomment")); + list.add(factory.create("porting")); + list.add(factory.create("XOcodeSize")); + list.add(factory.create("XTargetNearSource")); + list.add(factory.create("XaddSafePrefix")); + list.add( + factory.create( + "lenient", + "lenient", + Option.FORCE_PREFIXES, + false)); + list.add( + factory.create( + "strict", + "lenient", + Option.FORCE_PREFIXES, + false)); + ajc10Options = Collections.unmodifiableList(list); + + // -warn:.. and -g/-g:.. are not exclusive + if (!(factory.setupFamily("debug", true) + && factory.setupFamily("warning", true))) { + System.err.println("CompilerRun debug/warning fail!"); + } + Option[] options = + new Option[] { + crsIncrementalOption, + crsIgnoreWarnings, + crsSourceOption, + factory.create( + "Xlint", + "XLint", + Option.FORCE_PREFIXES, + true), + factory.create("verbose"), + factory.create("emacssym"), + factory.create("referenceInfo"), + factory.create("nowarn"), + factory.create("deprecation"), + factory.create("noImportError"), + factory.create("proceedOnError"), + factory.create("preserveAllLocals"), + factory.create( + "warn", + "warning", + Option.STANDARD_PREFIXES, + true), + factory.create( + "g", + "debug", + Option.STANDARD_PREFIXES, + false), + factory.create( + "g:", + "debug", + Option.STANDARD_PREFIXES, + true), + factory.create( + "1.3", + "compliance", + Option.FORCE_PREFIXES, + false), + factory.create( + "1.4", + "compliance", + Option.FORCE_PREFIXES, + false), + factory + .create( + "target", + "target", + Option.FORCE_PREFIXES, + false, + new String[][] { new String[] { + "1.1", + "1.2" }}), + factory.create("XnoInline"), + factory.create("XnoWeave"), + factory.create("XserializableAspects") + }; + for (int i = 0; i < options.length; i++) { + crsOptions.addOption(options[i]); + } + for (Iterator iter = compilerOptions.iterator(); + iter.hasNext(); + ) { + crsOptions.addOption((Option) iter.next()); + } + // these are recognized but records with them are skipped + for (Iterator iter = ajc10Options.iterator(); + iter.hasNext(); + ) { + crsOptions.addOption((Option) iter.next()); + } + crsOptions.freeze(); + } + + Options getOptions() { + return crsOptions; + } + + /** + * @return unmodifiable Set of options sharing the + * family "compiler". + */ + Set compilerOptions() { + return compilerOptions; + } + + /** + * @param option the compiler Option to get name for + * @return null if option is null or not a compiler option, + * or the fully-qualified classname of the ICommand + * implementing the compiler. + */ + String compilerClassName(Option option) { + if ((null == option) + || (!compilerFamily.sameFamily(option.getFamily()))) { + return null; + } + return (String) compilerOptionToClassname.get(option); + } + + /** + * Check that the compiler class associated with a compiler + * option can be loaded. This check only happens once; + * the result is cached (by compilerOption key) for later calls. + * @param compilerOption the Option (family compiler) to check + * @return true if compiler class for this option can be loaded + */ + boolean compilerIsLoadable(Option compilerOption) { + LangUtil.throwIaxIfNull(compilerOption, "compilerName"); + synchronized (compilerOptionToLoadable) { + Boolean result = + (Boolean) compilerOptionToLoadable.get( + compilerOption); + if (null == result) { + MessageHandler sink = new MessageHandler(); + String compilerClassname = + (String) compilerOptionToClassname.get( + compilerOption); + if (null == compilerClassname) { + result = Boolean.FALSE; + } else { + ICommand c = + ReflectionFactory.makeCommand( + compilerClassname, + sink); + + if ((null == c) + || sink.hasAnyMessage( + IMessage.ERROR, + true)) { + result = Boolean.FALSE; + } else { + result = Boolean.TRUE; + } + } + compilerOptionToLoadable.put( + compilerOption, + result); + } + return result.booleanValue(); + } + } + } // CompilerRun.Spec.CRSOptions } // CompilerRun.Spec -} // CompilerRun -
\ No newline at end of file +} // CompilerRun
\ No newline at end of file diff --git a/testing/src/org/aspectj/testing/harness/bridge/Globals.java b/testing/src/org/aspectj/testing/harness/bridge/Globals.java index 9b0b7736c..e4060d50a 100644 --- a/testing/src/org/aspectj/testing/harness/bridge/Globals.java +++ b/testing/src/org/aspectj/testing/harness/bridge/Globals.java @@ -12,7 +12,7 @@ * Xerox/PARC initial implementation * Wes Isberg removed unused globals. * ******************************************************************/ - + package org.aspectj.testing.harness.bridge; import java.io.File; @@ -23,71 +23,105 @@ import org.aspectj.util.LangUtil; /** */ public class Globals { - public static final String FORK_NAME = "harness.fork"; // XXX in testing-drivers/../package.htm + public static final String FORK_NAME = "harness.fork"; + // XXX in testing-drivers/../package.htm /** name/key of the System property to set to define library dir */ public static final String LIBDIR_NAME = "harness.libdir"; /** name/key of the System property to set to define J2SE_HOME dir */ public static final String J2SE14_RTJAR_NAME = "j2se14.rtjar"; + public static final String J2SE13_RTJAR_NAME = "j2se13.rtjar"; /** assumed relative location of a library with required jars */ - public static final String LIBDIR = getSystemProperty(LIBDIR_NAME, "../lib/test"); + public static final String LIBDIR = + getSystemProperty(LIBDIR_NAME, "../lib/test"); /** Path to J2SE_HOME */ - public static final File J2SE14_RTJAR; // XXX used only by 1.0 compiler tests - deprecate? + public static final File J2SE14_RTJAR; + // XXX used only by 1.0 compiler tests - deprecate? + public static final File J2SE13_RTJAR; /** array of parameter types for main(String[]) */ - public static final Class[] MAIN_PARM_TYPES = new Class[] {String[].class}; - public static final String S_testingclient_jar = LIBDIR + "/testing-client.jar"; - public static final String S_aspectjrt_jar = LIBDIR + "/aspectjrt.jar"; - public static final File F_testingclient_jar = new File(S_testingclient_jar); - public static final File F_aspectjrt_jar = new File(S_aspectjrt_jar); + public static final Class[] MAIN_PARM_TYPES = + new Class[] { String[].class }; + public static final String S_testingclient_jar = + LIBDIR + "/testing-client.jar"; + public static final String S_aspectjrt_jar = LIBDIR + "/aspectjrt.jar"; + public static final File F_testingclient_jar = + new File(S_testingclient_jar); + public static final File F_aspectjrt_jar = new File(S_aspectjrt_jar); public static final boolean globalsValid; - + static { + J2SE13_RTJAR = + getRtJarFor(J2SE13_RTJAR_NAME, "c:/home/apps/jdk13"); + J2SE14_RTJAR = + getRtJarFor(J2SE14_RTJAR_NAME, "c:/home/apps/jdk14"); + + String forkSpec = getSystemProperty(FORK_NAME, null); + globalsValid = + (FileUtil.canReadFile(F_testingclient_jar) + && FileUtil.canReadFile(F_aspectjrt_jar) + && FileUtil.canReadFile(J2SE13_RTJAR) + && FileUtil.canReadFile(J2SE14_RTJAR)); + } + + private static File getRtJarFor( + String propertyName, + String defaultLocation) { File j2seJar = null; - String path = getSystemProperty(J2SE14_RTJAR_NAME, "c:/home/apps/jdk14"); - File j2seHome = new File(path); - if (j2seHome.exists() && j2seHome.isDirectory()) { - File rtjar = new File(j2seHome.getAbsolutePath() + "/jre/lib/rt.jar"); - if (rtjar.canRead() && rtjar.isFile()) { - j2seJar = rtjar; + try { + String path = getSystemProperty(propertyName, defaultLocation); + File file = new File(path); + if (file.exists()) { + File rtjar = null; + if (path.endsWith("rt.jar")) { + rtjar = file; + } else if (file.isDirectory()) { + path = file.getAbsolutePath() + "/jre/lib/rt.jar"; + rtjar = new File(path); + } + if (rtjar.canRead() && rtjar.isFile()) { + j2seJar = rtjar; + } + } + } catch (Throwable t) { // avoid at all costs during static init + try { + t.printStackTrace(System.err); + } catch (Throwable x) { + // unable to log, better to just fail... } } - J2SE14_RTJAR = j2seJar; - globalsValid = - (FileUtil.canReadFile(F_testingclient_jar) - && FileUtil.canReadFile(F_aspectjrt_jar) - && FileUtil.canReadFile(J2SE14_RTJAR) - ); + return j2seJar; } - /** - * - * @return null if not found, or + /** + * + * @return null if not found, or * String with class path for compiler to load J2SE 1.4 classes from. - */ - public static String get14Bootclasspath() { + */ + public static String get14Bootclasspath() { return null; - } + } - /** - * Get System property completely safely. - * @param propertyName the String name of the property to get - * @param defaultValue the String default value to return value is null or empty - * @return String value or defaultValue if not available. - */ - static String getSystemProperty( - String propertyName, - String defaultValue) { + /** + * Get System property completely safely. + * @param propertyName the String name of the property to get + * @param defaultValue the String default value to return value is null or empty + * @return String value or defaultValue if not available. + */ + static String getSystemProperty( + String propertyName, + String defaultValue) { String result = defaultValue; try { String value = System.getProperty(propertyName); if (!LangUtil.isEmpty(value)) { result = value; } - } catch (Throwable t) {} - return result; - } + } catch (Throwable t) { + } + return result; + } } diff --git a/testing/src/org/aspectj/testing/util/options/Option.java b/testing/src/org/aspectj/testing/util/options/Option.java new file mode 100644 index 000000000..85917922e --- /dev/null +++ b/testing/src/org/aspectj/testing/util/options/Option.java @@ -0,0 +1,707 @@ +/* ******************************************************************* + * Copyright (c) 2003 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Wes Isberg initial implementation + * ******************************************************************/ + +package org.aspectj.testing.util.options; + +import java.util.*; + +import org.aspectj.util.LangUtil; + +/** + * Immutable schema for an input (command-line) option. + * The schema contains the expected name/label, + * the family (for comparison purposes), + * and permitted prefixes. + * This has operations to accept input values and compare options. + * Options cannot be created directly; for that, use an + * <code>Option.Factory</code>, since it enforces uniqueness + * within the families and options created by the factory. + * <p> + * Option is used with related nested classes to implement relations: + * <ul> + * <li>Option.Factory produces Option</li> + * <li>An Option has a set of Option.Prefixes, + * which are variants of Option.Prefix + * valid for the option (e.g., on/set, force-off, and force-on)</li> + * <li>Option evaluates input, produces Option.Value</li> + * <li>Related instances of Option share an Option.Family, + * which enforce option exclusion, etc.</li> + * </ul> + * The classes are nested as "friends" in order to hide private + * members (esp. constructors) that can be used within relations. + * Most of these classes are immutable. + */ +public class Option implements Comparable { + public static final Prefix ON = new Prefix("-", "on", true, false); + public static final Prefix NONE = new Prefix("", "none", true, false); + public static final Prefix FORCE_ON = + new Prefix("!", "force-on", true, true); + + public static final Prefix FORCE_OFF = + new Prefix("^", "force-off", false, true); + public static final Prefixes LITERAL_PREFIXES = + new Prefixes(new Prefix[] { NONE }); + public static final Prefixes STANDARD_PREFIXES = + new Prefixes(new Prefix[] { ON }); + public static final Prefixes FORCE_PREFIXES = + new Prefixes(new Prefix[] { ON, FORCE_ON, FORCE_OFF }); + + /** family is the key for comparing two options */ + private final Family family; + + /** label expected for the option */ + private final String name; + + /** unique identifier for option */ + private final String optionIdentifier; + + /** prefixes permitted for the option in input */ + private final Prefixes permittedPrefixes; + + /** if true, then match on input that has extra suffix beyond prefix and name */ + private final boolean acceptSuffixedInput; + + /** + * If true, no collision if there are multiple values + * that share the same family but not the same literal value + */ + private final boolean permitMultipleValues; + + /** int number of arguments expected after the option itself */ + private final int numArguments; + + /** + * If numArguments > 0, each element has a list of arguments + * permitted at that index from the initial matching value. + * Elements are not null. + */ + private final String[][] permittedArguments; + + private final int nameLength; + + /* + * Create a standard named boolean option, + * permitting force-on and force-off. + * @param name the String name of the option, e.g., "1.3" for "-1.3" + * @param family + * @param permittedPrefixes + * @param acceptSuffixedInput + * @param permittedArguments + */ + public Option( + String name, + Family family, + Prefixes permittedPrefixes, + boolean acceptSuffixedInput, + String[][] permittedArguments) { + LangUtil.throwIaxIfNull(name, "name"); + LangUtil.throwIaxIfNull(family, "family"); + LangUtil.throwIaxIfNull(permittedPrefixes, "permittedPrefixes"); + this.name = name; + this.nameLength = name.length(); + this.family = family; + this.permittedPrefixes = permittedPrefixes; + this.acceptSuffixedInput = acceptSuffixedInput; + this.permitMultipleValues = false; + if (LangUtil.isEmpty(permittedArguments)) { + permittedArguments = new String[][] { }; + // nominal, unused + } else { + String[][] temp = new String[permittedArguments.length][]; + for (int i = 0; i < temp.length; i++) { + String[] toCopy = permittedArguments[i]; + LangUtil.throwIaxIfNull(toCopy, "no permitted args"); + final int len = toCopy.length; + String[] variants = new String[len]; + System.arraycopy(toCopy, 0, variants, 0, len); + temp[i] = variants; + } + permittedArguments = temp; + } + this.permittedArguments = permittedArguments; + numArguments = permittedArguments.length; + optionIdentifier = family.familyName + "." + name; + } + + public int compareTo(Object other) { + Option option = (Option) other; + int diff = family.compareTo(option.family); + if (0 == diff) { + diff = name.compareTo(option.name); + } + return diff; + } + + public Family getFamily() { + return family; + } + + public boolean permitMultipleValues() { + return permitMultipleValues; + } + + /** + * @return int number of elements in this option, + * e.g., 0 for -g or 1 for -source 1.4 + */ + public int numArguments() { + return numArguments; + } + + /** + * If this value String represents a valid input for this option, + * then create and return the associated Value. + * + * @param value the Value created, or null if invalid + * @return Value if this value is permitted by this option + */ + public Value acceptValue(String value) { + Prefix prefix = hasPrefix(value); + if (null != prefix) { + if (value.startsWith(name, prefix.length())) { + value = value.substring(prefix.length()); + if (value.length() == nameLength) { + return new Value(value, prefix, this); + } else if (acceptSuffixedInput) { + return new Value(value, prefix, this); + } else { + return rejectingSuffixedInput(value); + } + } + } + return null; + } + + /** @return true if we have same option family */ + public boolean sameOptionFamily(Option other) { + return ((null != other) && other.family.equals(family)); + } + + /** @return true if we have same option family and name */ + public boolean sameOptionIdentifier(Option other) { + return (sameOptionFamily(other) && name.equals(other.name)); + } + + public String toString() { + return name; + } + + /** + * Called when ignoreSuffix is off but we got value with suffix. + */ + protected Value rejectingSuffixedInput(String value) { + return null; + } + + /** + * Verify that the input is permitted at this position. + * @param input the String input to check for validity + * @param position the int proposed position (0-based) + * for the input (position 0 is for first argument) + * @return null if this input is valid at this position, + * or a String error message otherwise. + */ + String validArgument(String input, int position) { + if (null == input) { + return "null input"; + } + // assert numArguments == permittedInput.length + if ((position < 0) || (position >= numArguments)) { + return "no input permitted at " + position; + } + String[] permitted = permittedArguments[position]; + for (int i = 0; i < permitted.length; i++) { + if (input.equals(permitted[i])) { + return null; + } + } + return input + " not permitted, expecting one of " + + Arrays.asList(permitted); + } + + String getName() { + return name; + } + Object getKey() { + return family; + } + + private String optionIdentifier() { + return optionIdentifier; + } + + private Prefix hasPrefix(String value) { + for (Iterator iter = permittedPrefixes.iterator(); + iter.hasNext(); + ) { + Prefix prefix = (Prefix) iter.next(); + if (-1 != prefix.prefixLength(value)) { + return prefix; + } + } + return null; + } + + /** + * An option family identifies a set of related options that + * might share no literal specification. + * E.g., the compiler family of options might include + * -ajc and -eclipse, and the debugInfo family of options + * might include -g and -g:vars. + * Option families may permit or avoid option collisions. + * <p> + * For subclasses to permit some collisions and not others, + * they should set permitMultipleFamilyValues to false + * and implement <code>doCollision(Option, Option)</code>. + * <p> + * This relies on Factory to ensure that familyName is + * a unique identifier for the factory. + */ + public static class Family implements Comparable { + + /** unique String identifier for this family */ + private final String familyName; + + /** if true, then report no collisions */ + private final boolean permitMultipleFamilyValues; + + protected Family( + String familyName, + boolean permitMultipleFamilyValues) { + this.familyName = familyName; + this.permitMultipleFamilyValues = permitMultipleFamilyValues; + } + + public int compareTo(Object arg0) { + Family family = (Family) arg0; + return familyName.compareTo(family.familyName); + } + + public boolean sameFamily(Family family) { + return ( + (null != family) && familyName.equals(family.familyName)); + } + + boolean permitMultipleFamilyValues() { + return permitMultipleFamilyValues; + } + + /** + * Options collide if they share the same family + * but are not the same, + * and multiple values are not permitted by the family. + * @param lhs the Option to compare with rhs + * @param rhs the Option to compare with lhs + * @return true if the two options collide, false otherwise + * @throws IllegalArgumentException if the input differ + * and share the same family, but this isn't it. + */ + public final boolean collision(Option lhs, Option rhs) { + if ((lhs == rhs) || (null == lhs) || (null == rhs)) { + return false; + } + Family lhsFamily = lhs.getFamily(); + Family rhsFamily = rhs.getFamily(); + if (!(lhsFamily.sameFamily(rhsFamily))) { + return false; + } + if (lhs.sameOptionIdentifier(rhs)) { + return false; + } + if (this != lhsFamily) { + String s = + "expected family " + this +" got family " + lhsFamily; + throw new IllegalArgumentException(s); + } + return doCollision(lhs, rhs); + } + + /** + * Subclasses implement this to resolve collisions on + * a case-by-case basis. Input are guaranteed to be + * non-null, different, and to share this family. + * This implementation returns + * <code>!permitMultipleFamilyValues</code>. + * + * @param lhs the Option to compare + * @param rhs the other Option to compare + * @return true if there is a collision. + */ + protected boolean doCollision(Option lhs, Option rhs) { + return !permitMultipleFamilyValues; + } + } + + /** + * A factory enforces a namespace on options. + * All options produced from a given factory are unique, + * as are all families. + * Once an option or family is created, it cannot be changed. + * To have a family permit multiple values + * (i.e., ignore collisions), set up the family before any + * associated options are created using + * <code>setupFamily(String, boolean)</code>. + */ + public static class Factory { + private final String factoryName; + + /** enforce uniqueness of family */ + private final Map familyNameToFamily = new TreeMap(); + + /** enforce uniqueness of options */ + private final ArrayList names = new ArrayList(); + + public Factory(String factoryName) { + this.factoryName = factoryName; + } + + /** + * Ensure that the family with this name has the + * specified permission. If the family does not exist, + * it is created. If it does, the permission is checked. + * If this returns false, there is no way to change the + * family permission. + * @param name the String identifier for the family + * @param permitMultipleValues the boolean permission whether to + * allow multiple values in this family + * @return true if family exists with this name and permission + */ + public boolean setupFamily( + String name, + boolean permitMultipleValues) { + LangUtil.throwIaxIfNull(name, "name"); + Family family; + synchronized (familyNameToFamily) { + family = (Family) familyNameToFamily.get(name); + if (null == family) { + family = new Family(name, permitMultipleValues); + familyNameToFamily.put(name, family); + } else if ( + permitMultipleValues + != family.permitMultipleFamilyValues) { + return false; + } + } + return true; + } + + /** + * Register a family with this factory. + * @return null if the family was successfully registered, + * or a String error otherwise + */ + public String registerFamily(Family family) { + if (null == family) { + return "null family"; + } + synchronized (familyNameToFamily) { + Family knownFamily = + (Family) familyNameToFamily.get(family.familyName); + if (null == knownFamily) { + familyNameToFamily.put(family.familyName, family); + } else if (!knownFamily.equals(family)) { + return "different family registered, have " + + knownFamily + + " registering " + + family; + } + } + + return null; + } + + public Option create(String name) { + return create(name, name, FORCE_PREFIXES, false); + } + + public Option create( + String name, + String family, + Prefixes permittedPrefixes, + boolean acceptSuffixedInput) { + return create( + name, + family, + permittedPrefixes, + acceptSuffixedInput, + (String[][]) null); + } + + public Option create( + String name, + String family, + Prefixes permittedPrefixes, + boolean acceptSuffixedInput, + String[][] permittedArguments) { + LangUtil.throwIaxIfNull(name, "name"); + LangUtil.throwIaxIfNull(family, "family"); + LangUtil.throwIaxIfNull( + permittedPrefixes, + "permittedPrefixes"); + Family resolvedFamily; + synchronized (familyNameToFamily) { + resolvedFamily = (Family) familyNameToFamily.get(family); + if (null == resolvedFamily) { + resolvedFamily = new Family(family, false); + familyNameToFamily.put(family, resolvedFamily); + } + } + Option result = + new Option( + name, + resolvedFamily, + permittedPrefixes, + acceptSuffixedInput, + permittedArguments); + synchronized (names) { + String optionIdentifier = result.optionIdentifier(); + if (names.contains(optionIdentifier)) { + String s = "not unique: " + result; + throw new IllegalArgumentException(s); + } else { + names.add(optionIdentifier); + } + } + return result; + } + + public String toString() { + return Factory.class.getName() + ": " + factoryName; + } + + private void checkUnique(Option result) { + String name = result.family + "." + result.name; + } + } + + /** + * The actual input value for an option. + * When an option takes arguments, all the arguments + * are absorbed/flattened into its value. + */ + public static class Value { + private static final String FLATTEN_DELIM = "_"; + + private static final int NOTARGUMENT = -1; + + private static String flatten(String prefix, String suffix) { + return prefix + FLATTEN_DELIM + suffix; + } + + private static String[] unflatten(Value value) { + if (value.argIndex == Value.NOTARGUMENT) { + return new String[] { value.value }; + } + StringTokenizer st = + new StringTokenizer(value.value, FLATTEN_DELIM); + String[] result = new String[st.countTokens()]; + // assert result.length == 1+inputIndex + for (int i = 0; i < result.length; i++) { + result[i] = st.nextToken(); + } + return result; + + } + + public final String value; + public final Prefix prefix; + public final Option option; + private final int argIndex; + + private Value( + String value, + Prefix prefix, + Option option) { + this(value, prefix, option, NOTARGUMENT); + } + private Value( + String value, + Prefix prefix, + Option option, + int argIndex) { + this.value = value; + this.prefix = prefix; + this.option = option; + this.argIndex = argIndex; + // asserts deferred - local clients only + // assert null != value + // assert null != prefix + // assert null != option + // assert 0 <= inputIndex + // assert inputIndex <= option.numArguments() + // assert {number of DELIM} == argIndex + } + + public String[] unflatten() { + return unflatten(this); + } + + /** + * Create new value same as this, but with new prefix. + * If the prefix is the same, return this. + * @param prefix the Prefix to convert to + * @return Value with new prefix - never null + */ + public Value convert(Prefix prefix) { + LangUtil.throwIaxIfNull(prefix, "prefix"); + if (this.prefix.equals(prefix)) { + return this; + } + return new Value( + this.value, + prefix, + this.option, + this.argIndex); + } + + /** + * + * @param other + * @return true if other == this for purposes of collisions + */ + public boolean sameValueIdentifier(Value other) { + return ( + (null != other) + && sameValueIdentifier(option, other.value)); + } + + public boolean sameValueIdentifier(Option option, String value) { + return ( + (null != option) + && this.option.sameOptionIdentifier(option) + && this.value.equals(value)); + } + + public boolean conflictsWith(Value other) { + return ( + (null != other) + && option.equals(other.option) + && (prefix.force == other.prefix.force) + && ((prefix.set != other.prefix.set) + || !value.equals(other.value))); + } + + public String toString() { + return option + "=" + prefix + value; + } + + final Value nextInput(String input) throws InvalidInputException { + final int index = argIndex + 1; + String err = option.validArgument(input, index); + if (null != err) { + throw new InvalidInputException(err, input, option); + } + return new Value(flatten(value, input), prefix, option, index); + } + } + + /** + * A bunch of prefixes. + */ + public static class Prefixes { + final List list; + private Prefixes(Prefix[] prefixes) { + if (LangUtil.isEmpty(prefixes)) { + list = Collections.EMPTY_LIST; + } else { + list = + Collections.unmodifiableList( + Arrays.asList( + LangUtil.safeCopy(prefixes, new Prefix[0]))); + } + } + public Iterator iterator() { + return list.iterator(); + } + } + + /** + * A permitted prefix for an option, mainly so that options + * "-verbose", "^verbose", and "!verbose" can be treated + * as variants (set, force-off, and force-on) of the + * same "verbose" option. + */ + public static class Prefix { + private final String prefix; + private final int prefixLength; + private final String name; + private final boolean set; + private final boolean force; + private Prefix( + String prefix, + String name, + boolean set, + boolean force) { + this.prefix = prefix; + this.name = name; + this.set = set; + this.force = force; + this.prefixLength = prefix.length(); + } + + /** + * Render a value for input if this is set. + * @param value the String to render as an input value + * @return null if value is null or option is not set, + * "-" + value otherwise + */ + public String render(String value) { + return ((!set || (null == value)) ? null : "-" + value); + } + + boolean forceOff() { + return force && !set; + } + boolean forceOn() { + return force && set; + } + public boolean isSet() { + return set; + } + private int length() { + return prefixLength; + } + private int prefixLength(String input) { + if ((null != input) && input.startsWith(prefix)) { + return length(); + } + return -1; + } + public String toString() { + return prefix; + } + } + + /** + * Thrown when an Option specifies required arguments, + * but the arguments are not available. + */ + public static class InvalidInputException extends Exception { + public final String err; + public final String input; + public final Option option; + InvalidInputException(String err, String input, Option option) { + super(err); + this.err = err; + this.input = input; + this.option = option; + } + public String getFullMessage() { + return "illegal input \"" + + input + + "\" for option " + + option + + ": " + + err; + } + } +} diff --git a/testing/src/org/aspectj/testing/util/options/Options.java b/testing/src/org/aspectj/testing/util/options/Options.java new file mode 100644 index 000000000..642ea0ca7 --- /dev/null +++ b/testing/src/org/aspectj/testing/util/options/Options.java @@ -0,0 +1,152 @@ +/* ******************************************************************* + * Copyright (c) 2003 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Wes Isberg initial implementation + * ******************************************************************/ + +package org.aspectj.testing.util.options; + +import java.util.*; + +import org.aspectj.util.LangUtil; + +/** + * A bunch of options that handle search boilerplate. + * This enforces an initialization phase by permitting + * options to be added only until frozen, and + * permitting matching only after frozen. + */ +public class Options { + + /** if true, then perform extra checks to debug problems */ + private static final boolean verifying = false; + private static final boolean FROZEN = true; + + /** + * List input unmatched by options, if any. + * @param input the String[] used to generate the values + * @param values the Option.Value[] found from the input + * @return null if no values are null, String list of missed otherwise + */ + public static String missedMatchError( + String[] input, + Values values) { + int[] missed = values.indexMissedMatches(); + LangUtil.throwIaxIfNull(input, "input"); + LangUtil.throwIaxIfNull(values, "values"); + LangUtil.throwIaxIfFalse( + input.length == values.length(), + "input is not matched by values"); + if (0 == missed.length) { + return null; + } + StringBuffer sb = new StringBuffer(); + sb.append("missed values: ["); + for (int i = 0; i < missed.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(missed[i] + ": " + input[missed[i]]); + } + sb.append("]"); + return sb.toString(); + } + + private final ArrayList options = new ArrayList(); + private final boolean stopAtFirstMatch; + private boolean frozen = !FROZEN; + + public Options(boolean stopAtFirstMatch) { + this.stopAtFirstMatch = stopAtFirstMatch; + } + + public void freeze() { + if (frozen != FROZEN) { + frozen = FROZEN; + } + } + + public boolean isFrozen() { + return (frozen == FROZEN); + } + + public void addOption(Option option) { + checkFrozen("adding option", !FROZEN); + LangUtil.throwIaxIfNull(option, "option"); + options.add(option); + } + + /** + * Associate options matched, if any, with input by index. + * If an input element is not matched, the corresponding + * result element will be null. + * If there are multi-argument options matched, then + * only the initial element will be non-null, but it + * will contain the accumulated value of the arguments. + * @param input the String[] of input + * @return Option.Value[] corresponding to input + * @throws Option.InvalidInputException when encountering + * invalid arguments to a matched multi-argument option. + */ + public Values acceptInput(String[] input) + throws Option.InvalidInputException { + checkFrozen("matching options", FROZEN); + if ((null == input) || (0 == input.length)) { + return Values.EMPTY; + } + Option.Value[] results = new Option.Value[input.length]; + for (int i = 0; i < input.length; i++) { + Option.Value result = firstMatch(input[i]); + final int index = i; + if (null != result) { + for (int len = result.option.numArguments(); + len > 0; + len--) { + i++; + if (i >= input.length) { + throw new Option.InvalidInputException( + "not enough arguments", + null, + result.option); + } + result = result.nextInput(input[i]); + } + } + results[index] = result; + } + return Values.wrapValues(results); + } + + private void checkFrozen(String actionLabel, boolean expectFrozen) { + if (expectFrozen != isFrozen()) { + if (null == actionLabel) { + actionLabel = "use"; + } + if (expectFrozen) { + actionLabel = "must freeze before " + actionLabel; + } else { + actionLabel = "frozen before " + actionLabel; + } + throw new IllegalStateException(actionLabel); + } + } + + private Option.Value firstMatch(String value) { + LangUtil.throwIaxIfNull(value, "value"); + ArrayList list = new ArrayList(); + for (Iterator iter = options.iterator(); iter.hasNext();) { + Option option = (Option) iter.next(); + Option.Value result = option.acceptValue(value); + if (null != result) { + return result; + } + } + return null; + } +} diff --git a/testing/src/org/aspectj/testing/util/options/Values.java b/testing/src/org/aspectj/testing/util/options/Values.java new file mode 100644 index 000000000..21cd3b52f --- /dev/null +++ b/testing/src/org/aspectj/testing/util/options/Values.java @@ -0,0 +1,714 @@ +/* ******************************************************************* + * Copyright (c) 2003 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Wes Isberg initial implementation + * ******************************************************************/ + +package org.aspectj.testing.util.options; + +import java.util.*; + +import org.aspectj.testing.util.options.Option.Value; +import org.aspectj.util.LangUtil; + +/** + * Wrapper for Value[] that handles search boilerplate. + */ +public class Values { + public static final Values EMPTY; + /** used by methods taking Selector to halt processing early */ + private static final boolean VERIFYING = true; + private static final boolean FIND_ALL = true; + + private static final String NO_ERROR = "no error"; + + static { + EMPTY = new Values(new Value[0]); + } + + public static Values wrapValues(Value[] values) { + if ((null == values) || (0 == values.length)) { + return EMPTY; + } + return new Values(values); + } + + public static Values wrapValues(Values[] values) { + if ((null == values) || (0 == values.length)) { + return EMPTY; + } + Value[] input = null; + if (values.length == 1) { + input = values[0].asArray(); + LangUtil.throwIaxIfNull(input, "values"); + } else { + int length = 0; + for (int i = 0; i < values.length; i++) { + if (values[i] == null) { + LangUtil.throwIaxIfNull(null, "null value[" + i + "]"); + } + length += values[i].length(); + } + input = new Value[length]; + length = 0; + Value[] temp; + for (int i = 0; i < values.length; i++) { + temp = values[i].asArray(); + System.arraycopy(temp, 0, input, length, temp.length); + length += temp.length; + } + } + return new Values(input); + } + + static int[] invert(int[] missed, int length) { + final int MAX = length; + final int len = MAX - missed.length; + final int[] result = new int[len]; + int missedIndex = 0; + int resultIndex = 0; + for (int counter = 0; counter < MAX; counter++) { + // catch result up to missed + while (((missedIndex >= missed.length) + || (missed[missedIndex] > counter)) + && (counter < MAX)) { + result[resultIndex++] = counter++; + } + // absorb missed up to counter + while ((missedIndex < missed.length) + && (missed[missedIndex] <= counter) + && (counter < MAX)) { + missedIndex++; + } + } + return result; + } + + private static Option.Value[] toArray(ArrayList list) { + return (Option.Value[]) list.toArray(new Option.Value[0]); + } + + /** + * Resolve input to remove any values matching the same options, + * where option conflicts are handled by option forcing. + * First, for any force-off value, all matching set-on and + * the force-off itself are removed. At this time, if there + * is a matching force-on, then this will return an error. + * Next, for any force-on value, it is converted to set-on, + * and any other matching set-on value is removed. + * Finally, this signals a collision if two values share + * the same option family and the family reports that this is + * a collision. + * In all cases, only the first error detected is reported. + * @param input the Option.Value[] matched from the input, + * (forced/duplicate options will be set to null) + * @return String error during resolution, or null if no error + */ + private static String resolve(Option.Value[] input) { + String err = null; + if (LangUtil.isEmpty(input)) { + return null; + } + + Map familyToMatches = new TreeMap(); + for (int i = 0;(null == err) && (i < input.length); i++) { + if (null != input[i]) { + Option.Family family = input[i].option.getFamily(); + int[] matches = (int[]) familyToMatches.get(family); + if (null == matches) { + matches = match(input, i); + familyToMatches.put(family, matches); + } + } + } + + familyToMatches = Collections.unmodifiableMap(familyToMatches); + for (Iterator iter = familyToMatches.entrySet().iterator(); + (null == err) && iter.hasNext(); + ) { + Map.Entry entry = (Map.Entry) iter.next(); + int[] matches = (int[]) entry.getValue(); + err = resolve(input, matches); + } + return err; + } + + /** + * Resolve all related options into one + * by nullifying or modifying the values. + * + * First, for any force-off value, + * remove all identical set-on + * and the force-off itself, + * and alert on any identical force-on. + * + * Next, for any force-on value, + * convert to set-on, + * throw Error on any same-family force-off value, + * remove any identical force-on or set-on value, + * alert on any other non-identical same-family force-on value, + * remove any same-family set-on value, + * and alert on any same-family set-off value. + * + * Finally, alert if any two remaining values share + * the same option family, unless the option is marked + * as permitting multiple values. + * + * @param input the Option.Value[] matching the input + * @param matches the int[] list of indexes into input for + * values for related by option + * (all such values must have option matched by family) + * @return String error, if any, or null if no error + * @see #match(Option.Value[], int) + */ + private static String resolve(Option.Value[] input, int[] matches) { + String err = null; + // seek force-off + Option.Value forceOff = null; + Option option = null; + // find and remove any force-off + for (int i = 0;(null == err) && (i < matches.length); i++) { + Option.Value value = input[matches[i]]; + if (null != value) { + // verify that matches are in the same family + if (VERIFYING) { + if (null == option) { + option = value.option; + } else if (!(option.sameOptionFamily(value.option))) { + String s = + value.option + + " has different family from " + + option; + throw new IllegalArgumentException(s); + } + } + if (value.prefix.forceOff()) { + err = removeForceOff(input, value, matches); + } + } + } + // find and set any force-on, removing others + for (int i = 0;(null == err) && (i < matches.length); i++) { + Option.Value value = input[matches[i]]; + if (null != value) { + if (value.prefix.forceOn()) { + err = convertForceOn(input, i, matches); + } + } + } + // remove any exact duplicates + for (int i = 0;(null == err) && (i < matches.length); i++) { + Option.Value value = input[matches[i]]; + if (null != value) { + for (int j = i + 1; j < matches.length; j++) { + if (value.sameValueIdentifier(input[matches[j]])) { + input[matches[j]] = null; + } + } + } + } + // signal error if two left unless permitMultipleFamilyValues + Option.Value first = null; + for (int i = 0;(null == err) && (i < matches.length); i++) { + Option.Value value = input[matches[i]]; + if (null != value) { + if (null == first) { + first = value; + if (first + .option + .getFamily() + .permitMultipleFamilyValues()) { + break; + } + } else { + err = "collision between " + first + " and " + value; + } + } + } + + return err; + } + + /** + * For any force-off value, + * remove all set-on or force-off with same value + * (including the force-off itself), + * and alert on any identical force-on. + * @param input the Option.Value[] matching the input + * @param value the force-off Option.Value to remove + * @param matches the int[] list of indexes into input for + * values for related by option + * (all such values must have matching option) + * @return String error if any + */ + private static String removeForceOff( + Option.Value[] input, + Option.Value value, + int[] matches) { + if (!value.prefix.forceOff()) { + throw new IllegalArgumentException( + "expecting force-off: " + value); + } + for (int i = 0; i < matches.length; i++) { + Option.Value match = input[matches[i]]; + if ((null != match) && value.sameValueIdentifier(match)) { + if (match.prefix.forceOn()) { + return "force conflict between " + + value + + " and " + + match; + } else { + input[matches[i]] = null; // unset matches[i]? + } + } + } + return null; + } + + /** + * For this force-on value, convert to set-on, + * throw Error on any same-family force-off value, + * remove any identical force-on or set-on value, + * alert on any other non-identical same-family force-on value, + * remove any same-family set-on value, + * and alert on any same-family set-off value. + * This must be called after <code>removeForceOff(..)</code>. + * @param input the Option.Value[] to modify + * @param valueIndex the int index in matches to find the force-on + * and to start after + * @param matches the int[] map into input entries with matching options + * @return + * @throw Error if any matching force-off found + */ + private static String convertForceOn( + Option.Value[] input, + int valueIndex, + int[] matches) { + Option.Value value = input[matches[valueIndex]]; + if (!value.prefix.forceOn()) { + throw new IllegalArgumentException( + "expecting force-on: " + value); + } + input[matches[valueIndex]] = value.convert(Option.ON); + for (int i = 0; i < matches.length; i++) { + if (i == valueIndex) { + continue; + } + Option.Value match = input[matches[i]]; + if (null != match) { + // assert match.sameOptionFamily(value); + if (match.prefix.forceOff()) { + throw new Error( + "unexpected force-off:" + + match + + " when processing " + + value); + } + if (value.option.sameOptionIdentifier(match.option)) { + input[matches[i]] = null; + // remove any identical force-on or set + } else if (match.prefix.forceOn()) { + return "conflict between " + match + " and " + value; + } else if (match.prefix.isSet()) { + input[matches[i]] = null; + // remove any same-value set-on value + } else { // same family, force-off + return "collision between " + match + " and " + value; + } + } + } + return null; + } + + /** + * Get a list of input matching the option in the initial value, + * rendered as indexes into the input array. + * @param input the Option.Value[] to seek in + * @param start the int index of the starting position + * @return int[] of indexes into input with the same option + * as index[start] - never null, but can be empty + */ + private static int[] match(Option.Value[] input, int start) { + IntList result = new IntList(); + Option.Family key = null; + Option.Family nextKey = null; + for (int i = start; i < input.length; i++) { + if (null != input[i]) { + nextKey = input[i].option.getFamily(); + if (null == key) { + key = nextKey; + result.add(i); + } else if (key.equals(nextKey)) { + result.add(i); + } + } + } + return result.getList(); + } + + static int nullify(Option.Value[] values, Selector selector) { + LangUtil.throwIaxIfNull(selector, "selector"); + int changed = 0; + for (int i = 0; i < values.length; i++) { + final boolean accepted; + try { + accepted = selector.accept(values[i]); + } catch (Error e) { + if (e != Selector.STOP) { + throw e; + } + break; + } + if (accepted) { + if (null != values[i]) { + values[i] = null; + changed++; + } + } + } + return changed; + } + + /** + * Render set values as String using associated prefix. + * @param values the Value[] to render + * @return String[] of values rendered for output + * (never null or longer than values, but might be shorter) + */ + private static String[] render(Value[] values) { + ArrayList list = new ArrayList(); + for (int i = 0; i < values.length; i++) { + if (null != values[i]) { + String[] output = values[i].unflatten(); + if (LangUtil.isEmpty(output)) { + throw new Error("no output for " + values[i]); + } + + String s = values[i].prefix.render(output[0]); + if (null != s) { // this means the prefix is set + list.add(s); + for (int j = 1; j < output.length; j++) { + list.add(output[j]); + } + } + } + } + return (String[]) list.toArray(new String[list.size()]); + } + + private final Option.Value[] values; + private Option.Value[] valuesNotNull; + private String resolveError; + + private Values(Value[] values) { + this.values = new Value[values.length]; + System.arraycopy(values, 0, this.values, 0, values.length); + } + + public int length() { + return values.length; + } + + public Option.Value[] asArray() { + Option.Value[] result = new Option.Value[values.length]; + System.arraycopy(values, 0, result, 0, result.length); + return result; + } + + /** + * Emit as String[] the non-null values. + * @return String[] of matched entries (never null, elements not null) + */ + public String[] render() { + return Values.render(valuesNotNull()); + } + + public String toString() { + return Arrays.asList(values).toString(); + } + + /** + * Create index into values of those that were matched, + * including the options and their arguments. + * @return int[] of elements in values that are not null (options) + * or that represent option arguments + */ + public int[] indexMatches() { + // must be in order, low to high + final int[] missed = indexMissedMatches(); + return invert(missed, length()); + } + + /** + * Create index into values of missed input, + * taking into account that matched arguments are + * represented as null. + * @return int[] of elements in values that are null + * or optionally represent option arguments + */ + public int[] indexMissedMatches() { + MissedSelector selector = new MissedSelector(); + find(selector, FIND_ALL); + String errors = selector.getErrors(); + if (null != errors) { + throw new Error(errors); + } + return selector.getResult(); + } + + public Value firstInFamily(Option.Family family) { + return findFirst(new ValueSelector(family)); + } + + public Value[] allInFamily(Option.Family family) { + return find(new ValueSelector(family), FIND_ALL); + } + + public Value firstOption(Option option) { + return findFirst(new ValueSelector(option)); + } + + public Value[] allOption(Option option) { + return find(new ValueSelector(option), FIND_ALL); + } + + public Value firstValue(Option option, String value) { + LangUtil.throwIaxIfNull(value, "value"); + return findFirst(new ValueSelector(option, value)); + } + + public Value[] allValues(Option option, String value) { + LangUtil.throwIaxIfNull(value, "value"); + return find(new ValueSelector(option, value), FIND_ALL); + } + + public boolean isResolved() { + return ((this != EMPTY) && (null != resolveError)); + } + + /** + * + * @param selector the Selector to pick out entries to nullify + * (should throw STOP to halt processing) + * @return Values resulting from nullifying entries, + * or this if none were changed + */ + public Values nullify(Selector selector) { + if (null == selector) { + return this; + } + Value[] temp = asArray(); + int changed = nullify(temp, selector); + if (0 == changed) { + return this; + } + return new Values(temp); + } + + /** + * Resolve options, removing duplicates by force if necessary. + * If any error is returned, then the values are left unchanged. + * @return String error, if any + * @throws IllegalStateException if <code>isResolved()</code> + */ + public String resolve() { + if (isResolved()) { + throw new IllegalStateException("already resolved"); + } + Option.Value[] temp = asArray(); + resolveError = resolve(temp); + if (null == resolveError) { + System.arraycopy(temp, 0, values, 0, temp.length); + valuesNotNull = null; + resolveError = NO_ERROR; + return null; + } + return resolveError; + } + + protected Option.Value findFirst(Selector filter) { + Option.Value[] result = find(filter, !FIND_ALL); + return (0 == result.length ? null : result[0]); + } + + protected Option.Value[] find(Selector filter, boolean findAll) { + LangUtil.throwIaxIfNull(filter, "filter"); + ArrayList result = new ArrayList(); + for (int i = 0; i < values.length; i++) { + final boolean accepted; + try { + accepted = filter.accept(values[i]); + } catch (Error e) { + if (Selector.STOP != e) { + throw e; + } + break; + } + if (accepted) { + result.add(values[i]); + if (findAll != FIND_ALL) { + break; + } + } + } + return toArray(result); + } + + private Option.Value[] valuesNotNull() { + if (null == valuesNotNull) { + ArrayList list = new ArrayList(); + for (int i = 0; i < this.values.length; i++) { + if (null != this.values[i]) { + list.add(this.values[i]); + } + } + valuesNotNull = toArray(list); + } + return valuesNotNull; + } + + public static class Selector { + public static final Error STOP = new Error("stop invoking Selector"); + protected Selector() { + } + protected boolean accept(Value value) { + return false; + } + } + protected static class ValueSelector extends Selector { + + private final Option option; + private final Option.Family family; + private final String value; + ValueSelector(Option.Family family) { + LangUtil.throwIaxIfNull(family, "family"); + this.family = family; + option = null; + value = null; + } + ValueSelector(Option option) { + this(option, (String) null); + } + ValueSelector(Option option, String value) { + LangUtil.throwIaxIfNull(option, "option"); + this.option = option; + family = null; + this.value = value; + } + protected boolean accept(Value value) { + if (null == value) { + return false; + } + if (null != family) { + return family.sameFamily(value.option.getFamily()); + } else if (!option.sameOptionIdentifier(value.option)) { + return false; + } else { + return ((null == this.value) + || (this.value.equals(value.value))); + } + } + } + + /** pick all null entries (except for args), return as int[] */ + protected static class MissedSelector extends Selector { + public static final String DELIM = "; "; + final IntList result = new IntList(); + int index; + final StringBuffer errors = new StringBuffer(); + int argsExpected; + Option argsExpectedFor; + MissedSelector() { + } + + int[] getResult() { + return result.getList(); + } + + /** + * add index if value is null + * unless skipArguments + */ + protected boolean accept(Value value) { + index++; + if (null != value) { + if (0 < argsExpected) { // expected more (null) args + missedArgsFor(argsExpectedFor, argsExpected); + } + argsExpected = value.option.numArguments(); + argsExpectedFor = value.option; + } else if (0 < argsExpected) { // ignore null in arg position + argsExpected--; + if (0 == argsExpected) { + argsExpectedFor = null; + } + } else { // null, not expecting arg, so missing + result.add(index - 1); + return true; + } + return false; + } + + private void missedArgsFor(Option option, int numArgs) { + errors.append("missed "); + errors.append(numArgs + " args for "); + errors.append(option + DELIM); + } + + String getErrors() { + if (0 < argsExpected) { + } + if (0 == errors.length()) { + return null; + } + return errors.toString(); + } + } + + static class IntList { + // not synchronized - used only in one thread + static String render(int[] input) { + if (null == input) { + return "null"; + } + StringBuffer sb = new StringBuffer(); + sb.append("["); + for (int i = 0; i < input.length; i++) { + if (i > 0) { + sb.append(", " + input[i]); + } else { + sb.append("" + input[i]); + } + } + sb.append("]"); + return sb.toString(); + } + + private int[] input = new int[256]; + private int insert; + private void add(int i) { + if (insert >= input.length) { + int[] temp = new int[insert + 256]; + for (int j = 0; j < input.length; j++) { + temp[j] = input[j]; + } + input = temp; + } + input[insert++] = i; + } + + private int[] getList() { + int[] result = new int[insert]; + for (int i = 0; i < result.length; i++) { + result[i] = input[i]; + } + return result; + } + } +} diff --git a/testing/testsrc/TestingModuleTests.java b/testing/testsrc/TestingModuleTests.java index 452d2e0b1..4c904ac90 100644 --- a/testing/testsrc/TestingModuleTests.java +++ b/testing/testsrc/TestingModuleTests.java @@ -25,6 +25,7 @@ public class TestingModuleTests extends TestCase { suite.addTest(org.aspectj.testing.harness.bridge.TestingBridgeTests.suite()); suite.addTest(org.aspectj.testing.taskdefs.TaskdefTests.suite()); suite.addTest(org.aspectj.testing.util.UtilTests.suite()); + suite.addTest(org.aspectj.testing.util.options.OptionsTests.suite()); suite.addTest(org.aspectj.testing.xml.TestingXmlTests.suite()); return suite; } diff --git a/testing/testsrc/org/aspectj/testing/harness/bridge/CompilerRunSpecTest.java b/testing/testsrc/org/aspectj/testing/harness/bridge/CompilerRunSpecTest.java index a876db105..8d05160b4 100644 --- a/testing/testsrc/org/aspectj/testing/harness/bridge/CompilerRunSpecTest.java +++ b/testing/testsrc/org/aspectj/testing/harness/bridge/CompilerRunSpecTest.java @@ -1,6 +1,6 @@ /* ******************************************************************* - * Copyright (c) 1999-2001 Xerox Corporation, - * 2002 Palo Alto Research Center, Incorporated (PARC). + * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC), + * 2003 Contributors. * All rights reserved. * This program and the accompanying materials are made available * under the terms of the Common Public License v1.0 @@ -9,14 +9,22 @@ * * Contributors: * Xerox/PARC initial implementation + * Wes Isberg 2003 modifications * ******************************************************************/ package org.aspectj.testing.harness.bridge; +import java.util.*; +import java.util.Arrays; + import junit.framework.TestCase; import org.aspectj.bridge.MessageHandler; import org.aspectj.bridge.MessageUtil; +import org.aspectj.testing.harness.bridge.CompilerRun.Spec.CRSOptions; +import org.aspectj.testing.util.options.*; +import org.aspectj.testing.util.options.Options; +import org.aspectj.util.LangUtil; /** * @@ -24,147 +32,515 @@ import org.aspectj.bridge.MessageUtil; public class CompilerRunSpecTest extends TestCase { private static boolean PRINTING = true; - - /** - * Constructor for CompilerRunSpecTest. - * @param name - */ - public CompilerRunSpecTest(String name) { - super(name); - } + private static final boolean SETUP_JAVA13 = + haveProperty(Globals.J2SE13_RTJAR_NAME); + private static final boolean SETUP_JAVA14 = + haveProperty(Globals.J2SE14_RTJAR_NAME); + static { + if (!SETUP_JAVA14) { + System.err.println( + "warning: set -D" + + Globals.J2SE14_RTJAR_NAME + + "=... in order to run all tests"); + } + if (!SETUP_JAVA13) { + System.err.println( + "warning: set -D" + + Globals.J2SE13_RTJAR_NAME + + "=... in order to run all tests"); + } + } + private static boolean haveProperty(String key) { + try { + return (null != System.getProperty(key)); + } catch (Throwable t) { + // ignore + } + return false; + } + + /** + * Constructor for CompilerRunSpecTest. + * @param name + */ + public CompilerRunSpecTest(String name) { + super(name); + } public void testSetupArgs() { - checkSetupArgs("verbose", false); - // XXX skipping since eclipse is default - // checkSetupArgs("lenient", false); - // checkSetupArgs("strict", false); - // checkSetupArgs("ajc", true); // XXX need to predict/test compiler selection - // eclipse-only checkSetupArgs("eclipse", true); + checkSetupArgs("verbose", false); + } + + public void testCompliance() { + // 1.3 should work + String specOptions = "-1.3"; + String[] globalOptions = new String[0]; + boolean expectAdopted = true; + String resultContains = "1.3"; + String messagesContain = null; + MessageHandler handler = + runTest( + specOptions, + globalOptions, + expectAdopted, + resultContains, + messagesContain); + checkMessages(handler, null); + + // 1.3 should work with collision? + globalOptions = new String[] { "-1.3" }; + resultContains = "1.3"; + handler = + runTest( + specOptions, + globalOptions, + expectAdopted, + resultContains, + messagesContain); + checkMessages(handler, null); + + // 1.4 should work + globalOptions = new String[0]; + specOptions = "-1.4"; + resultContains = "1.4"; + handler = + runTest( + specOptions, + globalOptions, + expectAdopted, + resultContains, + messagesContain); + checkMessages(handler, null); + + // compliance not checked for valid numbers, so -1.2 would pass + } + + private void checkMessages(MessageHandler handler, String contains) { + if (null == contains) { + if (0 != handler.numMessages(null, true)) { + assertTrue("" + handler, false); + } + } else { + String messages = "" + handler; + if (-1 == messages.indexOf(contains)) { + assertTrue(messages, false); + } + } + } + + /** @testcase check -target converts for 1.1 and 1.2, not others */ + public void testTarget() { + final boolean PASS = true; + final boolean FAIL = false; + checkSourceTargetVersionConversion("target", 1, PASS, null); + checkSourceTargetVersionConversion("target", 2, PASS, null); + checkSourceTargetVersionConversion( + "target", + 3, + FAIL, + "illegal input"); + } + + /** @testcase check -source converts for 1.3 and 1.4, not others */ + public void testSource() { + final boolean PASS = true; + final boolean FAIL = false; + if (SETUP_JAVA13) { + checkSourceTargetVersionConversion("source", 3, PASS, null); + } + if (SETUP_JAVA14) { + checkSourceTargetVersionConversion("source", 4, PASS, null); + } + + checkSourceTargetVersionConversion( + "source", + 2, + FAIL, + "not permitted"); + checkSourceTargetVersionConversion( + "source", + 6, + FAIL, + "not permitted"); + } + + public void testSourceOverride() { + if (SETUP_JAVA13 && SETUP_JAVA14) { + checkSourceTargetOverride("source", 3, 4); + checkSourceTargetOverride("source", 4, 3); + checkSourceTargetOverride("source", 3, 3); + checkSourceTargetOverride("source", 4, 4); + } + } + + public void testTargetOverride() { + checkSourceTargetOverride("target", 1, 2); + checkSourceTargetOverride("target", 2, 1); + checkSourceTargetOverride("target", 1, 1); + checkSourceTargetOverride("target", 2, 2); } - void checkSetupArgs(String arg, boolean isTestArg) { + public void testCompilerOptions() { + checkCompilerOption(null, CompilerRun.Spec.DEFAULT_COMPILER); + CRSOptions crsOptions = CompilerRun.Spec.testAccessToCRSOptions(); + Set options = crsOptions.compilerOptions(); + assertTrue(null != options); + StringBuffer notLoaded = new StringBuffer(); + for (Iterator iter = options.iterator(); iter.hasNext();) { + Option compilerOption = (Option) iter.next(); + if (!(crsOptions.compilerIsLoadable(compilerOption))) { + notLoaded.append(" " + compilerOption); + } else { + String className = crsOptions.compilerClassName(compilerOption); + String argValue = compilerOption.toString(); // XXX snoop + String arg = Option.ON.render(argValue); + checkCompilerOption(arg, className); + } + } + if (0 < notLoaded.length()) { + System.err.println( + getClass().getName() + + ".testCompilerOptions()" + + " warning: compiler options not tested because not loadable: " + + notLoaded); + } + } + + void checkCompilerOption(String arg, String className) { + MessageHandler handler = new MessageHandler(); + try { + CompilerRun.Spec spec = null; + try { + spec = new CompilerRun.Spec(); + } catch (Throwable t) { + t.printStackTrace(System.err); + } + assertTrue(spec != null); + AbstractRunSpec.RT parentRuntime = new AbstractRunSpec.RT(); + String result; + String expResult; + + if (null != arg) { + parentRuntime.setOptions(new String[] { arg }); + } + if (!spec.adoptParentValues(parentRuntime, handler)) { + if (0 != handler.numMessages(null, true)) { + assertTrue(handler.toString(), false); + } else { + assertTrue("adopt failed, but no messages", false); + } + } + result = "" + spec.testSetup.commandOptions; + assertEquals("[]", result); + assertEquals(className, spec.testSetup.compilerName); + } finally { + } + } + + public void testSpecOptions() { + Options options = CompilerRun.Spec.testAccessToOptions(); + OptionChecker optionChecker = new OptionChecker(options); + // known failures: extdirs, aspectpath, Xlintfile <file> + // progress, time, noExit, repeat <n>, + // help, + String[][] input = + new String[][] { + new String[] { "-verbose" }, + new String[] { "-incremental" }, + new String[] { "-emacssym" }, + new String[] { "-Xlint" }, + new String[] { "-Xlint:error" }, + new String[] { "-1.3" }, + new String[] { "-1.4" }, + new String[] { "-source", "1.3" }, + new String[] { "-source", "1.4" }, + new String[] { "-target", "1.1" }, + new String[] { "-target", "1.2" }, + new String[] { "-preserveAllLocals" }, + new String[] { "-referenceInfo" }, + new String[] { "-deprecation" }, + new String[] { "-noImportError" }, + new String[] { "-proceedOnError" } + }; + String[][] literalInput = + new String[][] { + new String[] { "-nowarn" }, + new String[] { "-warn:deprecated" }, + new String[] { "-warn:deprecated,unverified" }, + new String[] { "-warn:deprecated", "-warn:unusedLocals" }, + new String[] { "-g" }, + new String[] { "-g", "-g:none" }, + new String[] { "-g:vars,source" }, + new String[] { "-verbose", "-g:vars,source" }, + }; + // normal + for (int i = 0; i < input.length; i++) { + optionChecker.checkOptions(input[i], input[i]); + } + for (int i = 0; i < literalInput.length; i++) { + optionChecker.checkOptions(literalInput[i], literalInput[i]); + } + // force-on + String[][] forceInput = duplicate(input, "!"); + for (int i = 0; i < input.length; i++) { + optionChecker.checkOptions(forceInput[i], input[i]); + } + // force-off + forceInput = duplicate(input, "^"); + String[] none = new String[0]; + for (int i = 0; i < input.length; i++) { + optionChecker.checkOptions(forceInput[i], none); + } + } + + String[][] duplicate(String[][] input, String prefix) { + String[][] output = new String[input.length][]; + final int prefixLength = (null == prefix ? 0 : prefix.length()); + for (int i = 0; i < output.length; i++) { + int length = input[i].length; + output[i] = new String[length]; + if ((length > 0) && (prefixLength > 0)) { + System.arraycopy(input[i], 0, output[i], 0, length); + output[i][0] = + prefix + output[i][0].substring(prefixLength); + } + } + return output; + } + + public void checkSourceTargetOverride(String name, int from, int to) { + final String specOptions = "-" + name + ", 1." + from; + String[] globalOptions = new String[] { "!" + name, "1." + to }; + boolean expectAdopted = true; + String resultContains = "[-" + name + ", 1." + to; + String messagesContain = null; + MessageHandler handler = + runTest( + specOptions, + globalOptions, + expectAdopted, + resultContains, + messagesContain); + checkMessages(handler, null); + } + + void checkSourceTargetVersionConversion( + String name, + int i, + boolean expectPass, + String expectedErr) { + final String specOptions = "-" + name + ", 1." + i; + String[] globalOptions = new String[0]; + boolean expectAdopted = expectPass; + String resultContains = + !expectPass ? null : "[-" + name + ", 1." + i; + String messagesContain = expectedErr; + MessageHandler handler = + runTest( + specOptions, + globalOptions, + expectAdopted, + resultContains, + messagesContain); + } + + MessageHandler runTest( + String specOptions, + String[] globalOptions, + boolean expectAdopted, + String resultContains, + String messagesContain) { MessageHandler handler = new MessageHandler(); try { CompilerRun.Spec spec = new CompilerRun.Spec(); AbstractRunSpec.RT parentRuntime = new AbstractRunSpec.RT(); + + if (!LangUtil.isEmpty(specOptions)) { + spec.setOptions(specOptions); + } + if (!LangUtil.isEmpty(globalOptions)) { + parentRuntime.setOptions(globalOptions); + } + boolean adopted = + spec.adoptParentValues(parentRuntime, handler); + if (adopted != expectAdopted) { + String s = + (expectAdopted ? "not " : "") + + "adopted spec=" + + specOptions + + " globals=" + + (LangUtil.isEmpty(globalOptions) + ? "[]" + : Arrays.asList(globalOptions).toString()) + + " -- " + + handler; + assertTrue(s, false); + } + if (null != resultContains) { + String result = "" + spec.testSetup.commandOptions; + if (-1 == result.indexOf(resultContains)) { + assertTrue( + "expected " + resultContains + " got " + result, + false); + } + } + if (null != messagesContain) { + boolean haveMessages = + (0 != handler.numMessages(null, true)); + if (!haveMessages) { + assertTrue("expected " + messagesContain, false); + } else { + String messages = handler.toString(); + if (-1 == messages.indexOf(messagesContain)) { + assertTrue( + "expected " + + messagesContain + + " got " + + messages, + false); + } + } + } + return handler; + } finally { + } + } + + void checkSetupArgs(String arg, final boolean isTestArg) { + MessageHandler handler = new MessageHandler(); + try { + CompilerRun.Spec spec = null; + try { + spec = new CompilerRun.Spec(); + } catch (Throwable t) { + t.printStackTrace(System.err); + } + assertTrue(spec != null); + AbstractRunSpec.RT parentRuntime = new AbstractRunSpec.RT(); String result; String expResult; - + // -------- local set + // global - (set) does not change local-set + parentRuntime.setOptions(new String[] { "-" + arg }); + assertTrue(spec.adoptParentValues(parentRuntime, handler)); + if (0 != handler.numMessages(null, true)) { + assertTrue(handler.toString(), false); + } + result = "" + spec.testSetup.commandOptions; + expResult = (isTestArg ? "[]" : "[-" + arg + "]"); + assertTrue(result, expResult.equals(result)); + // global ^ (force-off) to disable spec.setOptions("-" + arg); - parentRuntime.setOptions(new String[] {"^" + arg}); + parentRuntime.setOptions(new String[] { "^" + arg }); assertTrue(spec.adoptParentValues(parentRuntime, handler)); if (0 != handler.numMessages(null, true)) { assertTrue(handler.toString(), false); } - result = ""+spec.testSetup.commandOptions; + result = "" + spec.testSetup.commandOptions; assertTrue(result, "[]".equals(result)); - + // global ! (force-on) does not change local-set - parentRuntime.setOptions(new String[] {"!" + arg}); + parentRuntime.setOptions(new String[] { "!" + arg }); assertTrue(spec.adoptParentValues(parentRuntime, handler)); if (0 != handler.numMessages(null, true)) { assertTrue(handler.toString(), false); } - result = ""+spec.testSetup.commandOptions; + result = "" + spec.testSetup.commandOptions; expResult = (isTestArg ? "[]" : "[-" + arg + "]"); - assertTrue(result, expResult.equals(result)); - - // global - (set) does not change local-set - parentRuntime.setOptions(new String[] {"-" + arg}); - assertTrue(spec.adoptParentValues(parentRuntime, handler)); - if (0 != handler.numMessages(null, true)) { - assertTrue(handler.toString(), false); + if (!expResult.equals(result)) { + assertTrue( + "expected " + expResult + " got " + result, + false); } - result = ""+spec.testSetup.commandOptions; - expResult = (isTestArg ? "[]" : "[-" + arg + "]"); - assertTrue(result, expResult.equals(result)); - + // global (unset) does not change local-set - parentRuntime.setOptions(new String[] {""}); + parentRuntime.setOptions(new String[] { "" }); assertTrue(spec.adoptParentValues(parentRuntime, handler)); if (0 != handler.numMessages(null, true)) { assertTrue(handler.toString(), false); } - result = ""+spec.testSetup.commandOptions; + result = "" + spec.testSetup.commandOptions; expResult = (isTestArg ? "[]" : "[-" + arg + "]"); assertTrue(result, expResult.equals(result)); - + // -------- local force-on // global ^ (force-off) conflicts with local force-on spec.setOptions("!" + arg); - parentRuntime.setOptions(new String[] {"^" + arg}); + parentRuntime.setOptions(new String[] { "^" + arg }); assertTrue(!spec.adoptParentValues(parentRuntime, handler)); assertTrue(0 != handler.numMessages(null, true)); handler.init(); - + // global ! (force-on) does not change local force-on - parentRuntime.setOptions(new String[] {"!" + arg}); + parentRuntime.setOptions(new String[] { "!" + arg }); assertTrue(spec.adoptParentValues(parentRuntime, handler)); if (0 != handler.numMessages(null, true)) { assertTrue(handler.toString(), false); } - result = ""+spec.testSetup.commandOptions; + result = "" + spec.testSetup.commandOptions; expResult = (isTestArg ? "[]" : "[-" + arg + "]"); assertTrue(result, expResult.equals(result)); - + // global - (set) does not change local force-on - parentRuntime.setOptions(new String[] {"-" + arg}); + parentRuntime.setOptions(new String[] { "-" + arg }); assertTrue(spec.adoptParentValues(parentRuntime, handler)); if (0 != handler.numMessages(null, true)) { assertTrue(handler.toString(), false); } - result = ""+spec.testSetup.commandOptions; + result = "" + spec.testSetup.commandOptions; expResult = (isTestArg ? "[]" : "[-" + arg + "]"); assertTrue(result, expResult.equals(result)); - + // global (unset) does not change local force-on - parentRuntime.setOptions(new String[] {""}); + parentRuntime.setOptions(new String[] { "" }); assertTrue(spec.adoptParentValues(parentRuntime, handler)); if (0 != handler.numMessages(null, true)) { assertTrue(handler.toString(), false); } - result = ""+spec.testSetup.commandOptions; + result = "" + spec.testSetup.commandOptions; expResult = (isTestArg ? "[]" : "[-" + arg + "]"); assertTrue(result, expResult.equals(result)); - - + // -------- local force-off // global ^ (force-off) does not change local force-off spec.setOptions("^" + arg); - parentRuntime.setOptions(new String[] {"^" + arg}); + parentRuntime.setOptions(new String[] { "^" + arg }); assertTrue(spec.adoptParentValues(parentRuntime, handler)); if (0 != handler.numMessages(null, true)) { assertTrue(handler.toString(), false); } - result = ""+spec.testSetup.commandOptions; + result = "" + spec.testSetup.commandOptions; assertTrue(result, ("[]").equals(result)); - + // global ! (force-on) conflicts with local force-off - parentRuntime.setOptions(new String[] {"!" + arg}); + parentRuntime.setOptions(new String[] { "!" + arg }); assertTrue(!spec.adoptParentValues(parentRuntime, handler)); assertTrue(0 != handler.numMessages(null, true)); handler.init(); - - // global - (set) overridden by local force-off // XXX?? - parentRuntime.setOptions(new String[] {"-" + arg}); + + // global - (set) overridden by local force-off + parentRuntime.setOptions(new String[] { "-" + arg }); assertTrue(spec.adoptParentValues(parentRuntime, handler)); if (0 != handler.numMessages(null, true)) { assertTrue(handler.toString(), false); } - result = ""+spec.testSetup.commandOptions; + result = "" + spec.testSetup.commandOptions; assertTrue(result, ("[]").equals(result)); - + // global (unset) does not change local force-off - parentRuntime.setOptions(new String[] {""}); + parentRuntime.setOptions(new String[] { "" }); assertTrue(spec.adoptParentValues(parentRuntime, handler)); if (0 != handler.numMessages(null, true)) { assertTrue(handler.toString(), false); } - result = ""+spec.testSetup.commandOptions; + result = "" + spec.testSetup.commandOptions; assertTrue(result, ("[]").equals(result)); + + // undefined whether global set overrides local set + // for different sibling options } finally { if (PRINTING && (0 < handler.numMessages(null, true))) { MessageUtil.print(System.err, handler, "checkSetupArgs: "); diff --git a/testing/testsrc/org/aspectj/testing/util/options/OptionChecker.java b/testing/testsrc/org/aspectj/testing/util/options/OptionChecker.java new file mode 100644 index 000000000..f2ae8acad --- /dev/null +++ b/testing/testsrc/org/aspectj/testing/util/options/OptionChecker.java @@ -0,0 +1,195 @@ +/* ******************************************************************* + * Copyright (c) 2003 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Wes Isberg initial implementation + * ******************************************************************/ + +package org.aspectj.testing.util.options; + +import java.util.Arrays; + +import junit.framework.Assert; + +import org.aspectj.testing.util.LangUtil; + +/** + * Checks that throw AssertionFailedError on failure. + * Subclasses reimplement <code>assertionFailed(String)</code> + * to handle failures differently. + */ +public class OptionChecker { + private final Options options; + + public OptionChecker(Options options) { + this.options = options; + LangUtil.throwIaxIfNull(options, "options"); + } + + /** + * Subclasses override this to throw different exceptions + * on assertion failures. + * This implementation delegates to + * <code>Assert.assertTrue(label, false)</code>. + * @param label the String message for the assertion + * + */ + public void assertionFailed(String label) { + Assert.assertTrue(label, false); + } + + public void checkAssertion(String label, boolean test) { + if (!test) { + assertionFailed(label); + } + } + + public void checkOptions(String[] input, String[] expected) { + checkOptions(input, expected, true); + } + + public void checkOptions( + String[] input, + String[] expected, + boolean resolve) { + Values values = getValues(input); + if (resolve) { + String err = values.resolve(); + checkAssertion("error: \"" + err + "\"", null == err); + } + String[] actual = values.render(); // Value.render(values); + checkEqual(expected, actual); + } + + public void checkOptionsNegative( + String[] input, + String expectedMissedMatchErr, + String expectedResolveErr) { + checkOptionsNegative( + input, + null, + expectedMissedMatchErr, + expectedResolveErr); + } + + public void checkOptionsNegative( + String[] input, + String expectedInValuesException, + String expectedMissedMatchErr, + String expectedResolveErr) { + Values values = + getValuesNegative(input, expectedInValuesException); + if (null == expectedInValuesException) { + String err = Options.missedMatchError(input, values); + checkContains(expectedMissedMatchErr, err); + err = values.resolve(); + checkContains(expectedResolveErr, err); + } + } + + private Values getValuesNegative( + String[] input, + String expectedInExceptionMessage, + Options options) { + try { + return options.acceptInput(input); + } catch (Option.InvalidInputException e) { + String m = e.getFullMessage(); + boolean ok = + (null != expectedInExceptionMessage) + && (-1 != m.indexOf(expectedInExceptionMessage)); + if (!ok) { + e.printStackTrace(System.err); + if (null != expectedInExceptionMessage) { + m = + "expected \"" + + expectedInExceptionMessage + + "\" in " + + m; + } + assertionFailed(m); + } + return null; // actually never executed + } + } + + private Values getValuesNegative( + String[] input, + String expectedInExceptionMessage) { + return getValuesNegative( + input, + expectedInExceptionMessage, + options); + } + + private Values getValues(String[] input, Options options) { + return getValuesNegative(input, null, options); + } + + private Values getValues(String[] input) { + return getValuesNegative(input, null); + } + + private void checkContains(String expected, String expectedIn) { + if (null == expected) { + if (null != expectedIn) { + assertionFailed("did not expect \"" + expectedIn + "\""); + } + } else { + if ((null == expectedIn) + || (-1 == expectedIn.indexOf(expected))) { + assertionFailed( + "expected \"" + + expected + + "\" in \"" + + expectedIn + + "\""); + } + } + } + + private String safeString(String[] ra) { + return (null == ra ? "null" : Arrays.asList(ra).toString()); + } + + private void checkEqual(String[] expected, String[] actual) { + if (!isEqual(expected, actual)) { + assertionFailed( + "expected \"" + + safeString(expected) + + "\" got \"" + + safeString(actual) + + "\""); + } + } + + private boolean isEqual(String[] expected, String[] actual) { + if (null == expected) { + return (null == actual ? true : false); + } else if (null == actual) { + return false; + } else if (expected.length != actual.length) { + return false; + } + for (int i = 0; i < actual.length; i++) { + String e = expected[i]; + String a = actual[i]; + if (null == e) { + if (null != a) { + return false; + } + } else if (null == a) { + return false; + } else if (!(e.equals(a))) { + return false; + } + } + return true; + } + +} diff --git a/testing/testsrc/org/aspectj/testing/util/options/OptionsTest.java b/testing/testsrc/org/aspectj/testing/util/options/OptionsTest.java new file mode 100644 index 000000000..29048aa07 --- /dev/null +++ b/testing/testsrc/org/aspectj/testing/util/options/OptionsTest.java @@ -0,0 +1,486 @@ +/* ******************************************************************* + * Copyright (c) 2003 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Wes Isberg initial implementation + * ******************************************************************/ + +package org.aspectj.testing.util.options; + +import junit.framework.*; + +import org.aspectj.testing.util.options.Option.InvalidInputException; + +/** + */ +public class OptionsTest extends TestCase { + + private static Options OPTIONS; + + private static Options getOptions() { + if (null == OPTIONS) { + OPTIONS = new Options(true); + Option.Factory factory = new Option.Factory("OptionsTest"); + OPTIONS.addOption(factory.create("verbose")); + OPTIONS.addOption(factory.create("quiet")); + OPTIONS.addOption(factory.create("debug")); + OPTIONS.addOption( + factory.create("ajc", "compiler", Option.FORCE_PREFIXES, false)); + OPTIONS.addOption( + factory.create( + "eclipse", + "compiler", + Option.FORCE_PREFIXES, + false)); + OPTIONS.addOption( + factory.create( + "ajdeCompiler", + "compiler", + Option.FORCE_PREFIXES, + false)); + OPTIONS.addOption( + factory.create("1.3", "compliance", Option.FORCE_PREFIXES, false)); + OPTIONS.addOption( + factory.create("1.4", "compliance", Option.FORCE_PREFIXES, false)); + + // treating multi-arg as single - extrinsic flatten/unflatten + OPTIONS.addOption( + factory.create("target11", "target", Option.FORCE_PREFIXES, false)); + OPTIONS.addOption( + factory.create("target12", "target", Option.FORCE_PREFIXES, false)); + OPTIONS.addOption( + factory.create("source13", "source", Option.FORCE_PREFIXES, false)); + OPTIONS.addOption( + factory.create("source14", "source", Option.FORCE_PREFIXES, false)); + + // suffix options (a) -warn:... (b) -g, -g:... + Assert.assertTrue(factory.setupFamily("warning", true)); + OPTIONS.addOption( + factory.create("warn", "warning", Option.STANDARD_PREFIXES, true)); + + Assert.assertTrue(factory.setupFamily("debugSymbols", true)); + OPTIONS.addOption( + factory.create( + "g", + "debugSymbols", + Option.STANDARD_PREFIXES, + false)); + OPTIONS.addOption( + factory.create( + "g:", + "debugSymbols", + Option.STANDARD_PREFIXES, + true)); + + // treating multi-arg as single - intrinsic flatten/unflatten + OPTIONS + .addOption( + factory + .create( + "target", + "target", + Option.FORCE_PREFIXES, + false, + new String[][] { + new String[] { "1.1", "1.2" } + })); + OPTIONS + .addOption( + factory + .create( + "source", + "source", + Option.FORCE_PREFIXES, + false, + new String[][] { + new String[] { "1.3", "1.4" } + })); + OPTIONS.freeze(); + } + return OPTIONS; + } + + private boolean verbose; + private OptionChecker localOptionChecker; + + public void testDebugCase() { + OptionChecker optionChecker = getOptionChecker(); + String[] input = new String[] { + }; + String[] expected = new String[0]; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-target", "1.1", "^target", "1.2" }; + expected = new String[] { "-target", "1.1" }; + + optionChecker.checkOptions(input, expected); + } + public void testNegDebugCase() { + OptionChecker optionChecker = getOptionChecker(); + String[] input = new String[] { "-target" }; + String expectedInValuesException = "not enough arguments"; + String expectedMissedMatchErr = null; + String expectedResolveErr = null; + + optionChecker.checkOptionsNegative( + input, + expectedInValuesException, + expectedMissedMatchErr, + expectedResolveErr); + } + + public void testOptionsPositive() { + OptionChecker optionChecker = getOptionChecker(); + String[] input = new String[] { + }; + String[] expected = new String[0]; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-verbose" }; + expected = new String[] { "-verbose" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!verbose" }; + expected = new String[] { "-verbose" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!verbose", "-verbose" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-verbose", "!verbose" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "^verbose" }; + expected = new String[] { + }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "^verbose", "-verbose" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-verbose", "^verbose" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-verbose", "-debug" }; + expected = new String[] { "-verbose", "-debug" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!verbose", "!debug" }; + expected = new String[] { "-verbose", "-debug" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-verbose", "^verbose", "!debug" }; + expected = new String[] { "-debug" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "^verbose", "-verbose", "!debug" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!debug", "^verbose", "-verbose" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-ajc" }; + expected = new String[] { "-ajc" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-eclipse" }; + expected = new String[] { "-eclipse" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-eclipse", "-ajc", "!ajc" }; + expected = new String[] { "-ajc" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-eclipse", "!ajc" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-ajdeCompiler", "^ajc" }; + expected = new String[] { "-ajdeCompiler" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!ajdeCompiler", "^ajc" }; + optionChecker.checkOptions(input, expected); + + input = + new String[] { + "-verbose", + "^verbose", + "!quiet", + "-quiet", + "-debug", + "^debug" }; + expected = new String[] { "-quiet" }; + optionChecker.checkOptions(input, expected); + + input = + new String[] { + "-verbose", + "^debug", + "!quiet", + "!quiet", + "-debug", + "^verbose" }; + expected = new String[] { "-quiet" }; + optionChecker.checkOptions(input, expected); + } + + public void testOptionsNegative() { + OptionChecker optionChecker = getOptionChecker(); + String[] input = new String[] { "-unknown" }; + String expectedMissedMatchErr = "-unknown"; + String expectedResolveErr = null; + optionChecker.checkOptionsNegative( + input, + expectedMissedMatchErr, + expectedResolveErr); + + input = new String[] { "!verbose", "^verbose" }; + expectedMissedMatchErr = null; + expectedResolveErr = "conflict"; + optionChecker.checkOptionsNegative( + input, + expectedMissedMatchErr, + expectedResolveErr); + + input = new String[] { "!ajc", "!eclipse" }; + optionChecker.checkOptionsNegative( + input, + expectedMissedMatchErr, + expectedResolveErr); + + input = new String[] { "-ajc", "-eclipse" }; + expectedResolveErr = "collision"; + optionChecker.checkOptionsNegative( + input, + expectedMissedMatchErr, + expectedResolveErr); + + input = new String[] { "-verbose", "-verbose" }; + expectedResolveErr = null; // duplicates redundant, not colliding + optionChecker.checkOptionsNegative( + input, + expectedMissedMatchErr, + expectedResolveErr); + } + + public void testMissedMatches() throws InvalidInputException { + checkMissedMatches(new int[0], Values.EMPTY); + checkMissedMatches(new int[] { 0 }, + Values.wrapValues(new Option.Value[1])); // null in [0] + checkMissedMatches( + new int[] { 0, 1, 2 }, + Values.wrapValues(new Option.Value[] { null, null, null })); + + Option.Factory factory = new Option.Factory("testMissedMatches"); + Option single = factory.create("verbose"); + Option multiple = + factory + .create( + "source", + "source", + Option.STANDARD_PREFIXES, + false, + new String[][] { new String[] { "1.3", "1.4" } + }); + + Options options = new Options(false); + options.addOption(single); + options.addOption(multiple); + options.freeze(); + int[] expectNone = new int[0]; + String[] input = new String[] { "-verbose" }; + Values result = options.acceptInput(input); + checkMissedMatches(expectNone, result); + + input = new String[] { "-verbose", "-verbose" }; + result = options.acceptInput(input); + checkMissedMatches(expectNone, result); + + input = new String[] { "-source", "1.3" }; + result = options.acceptInput(input); + checkMissedMatches(expectNone, result); + + input = new String[] { "-source", "1.4" }; + result = options.acceptInput(input); + checkMissedMatches(expectNone, result); + + input = new String[] { "-verbose", "-missed" }; + result = options.acceptInput(input); + checkMissedMatches(new int[] { 1 }, result); + + input = new String[] { "-source", "1.4", "-missed" }; + result = options.acceptInput(input); + checkMissedMatches(new int[] { 2 }, result); + + input = new String[] { "-source", "1.4", "-missed", "-verbose" }; + result = options.acceptInput(input); + checkMissedMatches(new int[] { 2 }, result); + + } + + void checkMissedMatches(int[] expected, Values actual) { + int[] result = actual.indexMissedMatches(); + boolean failed = (result.length != expected.length); + + for (int i = 0; !failed && (i < result.length); i++) { + failed = (result[i] != expected[i]); + } + if (failed) { + assertTrue( + "expected " + + Values.IntList.render(expected) + + " got " + + Values.IntList.render(result) + + " for " + + actual, + false); + } + } + + public void testComplexOptionsPositive() { + OptionChecker optionChecker = getOptionChecker(); + String[] input = new String[] { + }; + String[] expected = new String[0]; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-target11" }; + expected = new String[] { "-target11" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!target12" }; + expected = new String[] { "-target12" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!target12", "-target11" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!target12", "^target11" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "^target12", "^target11" }; + expected = new String[] { + }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!1.3" }; + expected = new String[] { "-1.3" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!1.3", "-1.4" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!1.3", "^1.4" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "^1.3", "^1.4" }; + expected = new String[] { + }; + optionChecker.checkOptions(input, expected); + + } + + public void testMultiArgOptionsPositive() { + OptionChecker optionChecker = getOptionChecker(); + String[] input = new String[] { + }; + String[] expected = new String[0]; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-target", "1.1" }; + expected = new String[] { "-target", "1.1" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!target", "1.1" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!target", "1.1", "-target", "1.2" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-target", "1.1", "^target", "1.2" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-source", "1.3" }; + expected = new String[] { "-source", "1.3" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "!source", "1.3", "-source", "1.4" }; + optionChecker.checkOptions(input, expected); + + input = new String[] { "-source", "1.3", "^source", "1.4" }; + input = + new String[] { + "-source", + "1.3", + "^source", + "1.4", + "!source", + "1.3", + "-source", + "1.4" }; + optionChecker.checkOptions(input, expected); + } + + public void testMultiArgOptionsNegative() { + OptionChecker optionChecker = getOptionChecker(); + String[] input = new String[] { "-target" }; + String expectedException = "not enough arguments"; + + optionChecker.checkOptionsNegative( + input, + expectedException, + null, + null); + + input = new String[] { "-source" }; + optionChecker.checkOptionsNegative( + input, + expectedException, + null, + null); + + input = new String[] { "-source", "1.1" }; + expectedException = "not permitted"; + optionChecker.checkOptionsNegative( + input, + expectedException, + null, + null); + + input = new String[] { "-target", "1.3" }; + optionChecker.checkOptionsNegative( + input, + expectedException, + null, + null); + } + + public void testMultipleInput() { + OptionChecker optionChecker = getOptionChecker(); + String[][] input = + new String[][] { + new String[] { "-warn:deprecated" }, + new String[] { "-warn:deprecated,unverified" }, + new String[] { "-warn:deprecated", "-warn:unusedLocals" }, + new String[] { "-g" }, + new String[] { "-g", "-g:none" }, + new String[] { "-g:vars,source" }, + new String[] { "-verbose", "-g:vars,source" }, + }; + for (int i = 0; i < input.length; i++) { + optionChecker.checkOptions(input[i], input[i]); + } + } + + private OptionChecker getOptionChecker() { + if (null == localOptionChecker) { + localOptionChecker = new OptionChecker(getOptions()); + } + return localOptionChecker; + } +} diff --git a/testing/testsrc/org/aspectj/testing/util/options/OptionsTests.java b/testing/testsrc/org/aspectj/testing/util/options/OptionsTests.java new file mode 100644 index 000000000..2cc937935 --- /dev/null +++ b/testing/testsrc/org/aspectj/testing/util/options/OptionsTests.java @@ -0,0 +1,30 @@ +/* ******************************************************************* + * Copyright (c) 2003 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Wes Isberg initial implementation + * ******************************************************************/ + + +package org.aspectj.testing.util.options; + +import junit.framework.*; + +public class OptionsTests extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(OptionsTests.class.getName()); + //$JUnit-BEGIN$ + suite.addTestSuite(OptionsTest.class); + //$JUnit-END$ + return suite; + } + + public OptionsTests(String name) { super(name); } + +} diff --git a/testing/testsrc/org/aspectj/testing/util/options/ValuesTest.java b/testing/testsrc/org/aspectj/testing/util/options/ValuesTest.java new file mode 100644 index 000000000..cc6623ffc --- /dev/null +++ b/testing/testsrc/org/aspectj/testing/util/options/ValuesTest.java @@ -0,0 +1,69 @@ +/* ******************************************************************* + * Copyright (c) 2003 Contributors. + * All rights reserved. + * This program and the accompanying materials are made available + * under the terms of the Common Public License v1.0 + * which accompanies this distribution and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * Wes Isberg initial implementation + * ******************************************************************/ + +package org.aspectj.testing.util.options; + +import junit.framework.TestCase; + +/** + */ +public class ValuesTest extends TestCase { + + public ValuesTest(String s) { + super(s); + } + public void testInvert() { + checkInvert(new int[0], 0, new int[0]); // no input or missed => none found + checkInvert(new int[0], 1, new int[] {0}); // no missed, input 1 => 1 found + checkInvert(new int[] {0}, 1, new int[] {}); // 1 (all) missed, input 1 => none found + checkInvert(new int[] {}, 1, new int[] {0}); // 0 (none) missed, input 1 => 1 found + checkInvert(new int[] {1,2}, 3, new int[] {0}); // 2 missed, input 3 => 1 (first) found + checkInvert(new int[] {0,2}, 3, new int[] {1}); // 2 missed, input 3 => 1 (middle) found + checkInvert(new int[] {0,1}, 3, new int[] {2}); // 2 missed, input 3 => 1 (last) found + checkInvert(new int[] {1,3}, 4, new int[] {0,2}); // 2 missed, input 4 => 2 found + checkInvert(new int[] {5,6,7}, 8, new int[] {0,1,2,3,4}); // starting run + checkInvert(new int[] {0,1,2,3,4}, 8, new int[] {5,6,7}); // ending run + checkInvert(new int[] {0,5,6,7}, 8, new int[] {1,2,3,4}); // middle run + checkInvert(new int[] {0,5,6,9},10, new int[] {1,2,3,4,7,8}); // two middle run + checkInvert(new int[] {1,2,5,6,9},10, new int[] {0,3,4,7,8}); // start, 2 middle run + checkInvert(new int[] {0,1,2,5,6},10, new int[] {3,4,7,8,9}); // middle, end run + } + + void checkInvert(int[] missed, int length, int[] expected) { + int[] actual = Values.invert(missed, length); + assertTrue(null != actual); + assertTrue(actual.length == expected.length); + for (int i = 0; i < actual.length; i++) { + if (expected[i] != actual[i]) { + assertTrue("failed at " + i + render(expected, actual), false); + } + } + } + static String render(int[] expected, int[] actual) { + StringBuffer sb = new StringBuffer(); + sb.append(" expected "); + render(expected, sb); + sb.append(" actual "); + render(actual, sb); + return sb.toString(); + } + static void render(int[] ra, StringBuffer sb) { + sb.append("["); + for (int i = 0; i < ra.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append("" + ra[i]); + } + sb.append("]"); + } +} |