123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591 |
- /* *******************************************************************
- * Copyright (c) 1999-2001 Xerox Corporation,
- * 2002 Palo Alto Research Center, Incorporated (PARC).
- * All rights reserved.
- * This program and the accompanying materials are made available
- * under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Xerox/PARC initial implementation
- * ******************************************************************/
-
- package org.aspectj.testing.harness.bridge;
-
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileReader;
- import java.io.IOException;
- import java.io.Reader;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.StringTokenizer;
-
- //import org.aspectj.bridge.*;
- import org.aspectj.bridge.IMessageHandler;
- import org.aspectj.bridge.ISourceLocation;
- import org.aspectj.testing.run.IRunIterator;
- import org.aspectj.testing.run.IRunStatus;
- import org.aspectj.testing.run.Runner;
- import org.aspectj.testing.xml.XMLWriter;
- import org.aspectj.util.LangUtil;
-
- /**
- * An AjcTest has child subruns (compile, [inc-compile|run]*).
- * XXX title keys shared between all instances
- * (add Thread to key to restrict access?)
- */
- public class AjcTest extends RunSpecIterator {
-
- /** Unwrap an AjcTest.Spec from an IRunStatus around an AjcTest */
- public static Spec unwrapSpec(IRunStatus status) {
- if (null != status) {
- Object id = status.getIdentifier();
- if (id instanceof Runner.IteratorWrapper) {
- IRunIterator iter = ((Runner.IteratorWrapper) id).iterator;
- if (iter instanceof AjcTest) {
- return (Spec) ((AjcTest) iter).spec;
- }
- }
- }
- return null;
- }
-
- /** Unwrap initial CompilerRun.Spec from an AjcTest.Spec */
- public static CompilerRun.Spec unwrapCompilerRunSpec(Spec spec) {
- if (null != spec) {
- List kids = spec.getChildren();
- if (0 < kids.size()) {
- Object o = kids.get(0);
- if (o instanceof CompilerRun.Spec) {
- return (CompilerRun.Spec) o;
- }
- }
- }
- return null;
- }
-
- /** The spec creates the sandbox, so we use it throughout */
- public AjcTest(Spec spec, Sandbox sandbox, Validator validator) {
- super(spec, sandbox, validator, true);
- }
-
- /**
- * Clear the command from the sandbox, to avoid memory leaks.
- * @see org.aspectj.testing.harness.bridge.RunSpecIterator#iterationCompleted()
- */
- public void iterationCompleted() {
- super.iterationCompleted();
- sandbox.clearCommand(this);
- }
-
-
- /**
- * Specification for an ajc test.
- * Keyword directives are global/parent options passed, e.g., as
- * <pre>-ajctest[Require|Skip]Keywords=keyword{,keyword}..</pre>.
- * See VALID_SUFFIXES for complete list.
- */
- public static class Spec extends AbstractRunSpec {
- public static final String XMLNAME = "ajc-test";
- /**
- * do description as title, do sourceLocation,
- * do keywords, do options, skip paths, do comment,
- * skip staging, skip badInput,
- * skip dirChanges, do messages and do children
- * (though we do children directly).
- */
- private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT,
- "title", null, null, null, "", null, "", "", true, false, false);
-
- private static final String OPTION_PREFIX = "-ajctest";
- private static final String[] VALID_OPTIONS = new String[] { OPTION_PREFIX };
-
- private static final String TITLE_LIST = "TitleList=";
- private static final String TITLE_FAIL_LIST = "TitleFailList=";
- private static final String TITLE_CONTAINS= "TitleContains=";
- private static final String REQUIRE_KEYWORDS = "RequireKeywords=";
- private static final String SKIP_KEYWORDS = "SkipKeywords=";
- private static final String PICK_PR = "PR=";
- private static final List<String> VALID_SUFFIXES
- = Collections.unmodifiableList(Arrays.asList(new String[]
- { TITLE_LIST, TITLE_FAIL_LIST, TITLE_CONTAINS,
- REQUIRE_KEYWORDS, SKIP_KEYWORDS, PICK_PR }));
-
- /** Map String titlesName to List (String) of titles to accept */
- private static final Map<String,List<String>> TITLES = new HashMap<String,List<String>>();
-
- private static List<String> getTitles(String titlesName) {
- return getTitles(titlesName, false);
- }
- private static List<String> getTitles(String titlesName, boolean fail) {
- if (LangUtil.isEmpty(titlesName)) {
- return Collections.emptyList();
- }
- List<String> result = (List<String>) TITLES.get(titlesName);
- if (null == result) {
- result = makeTitlesList(titlesName, fail);
- TITLES.put(titlesName, result);
- }
- return result;
- }
-
- /**
- * Make titles list per titlesKey, either a path to a file
- * containing "[PASS|FAIL] {title}(..)" entries,
- * or a comma-delimited list of titles.
- * @param titlesKey a String, either a path to a file
- * containing "[PASS|FAIL] {title}(..)" entries,
- * or a comma-delimited list of titles.
- * @param fail if true, only read titles prefixed "FAIL" from files
- * @return the unmodifiable List of titles (maybe empty, never null)
- */
- private static List<String> makeTitlesList(String titlesKey, boolean fail) {
- File file = new File(titlesKey);
- return file.canRead()
- ? readTitlesFile(file, fail)
- : parseTitlesList(titlesKey);
- }
-
- /**
- * Parse list of titles from comma-delmited list
- * titlesList, trimming each entry and permitting
- * comma to be escaped with '\'.
- * @param titlesList a comma-delimited String of titles
- * @return the unmodifiable List of titles (maybe empty, never null)
- */
- private static List<String> parseTitlesList(String titlesList) {
- ArrayList<String> result = new ArrayList<String>();
- String last = null;
- StringTokenizer st = new StringTokenizer(titlesList, ",");
- while (st.hasMoreTokens()) {
- String next = st.nextToken().trim();
- if (next.endsWith("\\")) {
- next = next.substring(0, next.length()-1);
- if (null == last) {
- last = next;
- } else {
- last += next;
- }
- next = null;
- } else if (null != last) {
- next = (last + next).trim();
- last = null;
- } else {
- next = next.trim();
- }
- if (!LangUtil.isEmpty(next)) {
- result.add(next);
- }
- }
- if (null != last) {
- String m = "unterminated entry \"" + last; // XXX messages
- System.err.println(m + "\" in " + titlesList);
- result.add(last.trim());
- }
- return Collections.unmodifiableList(result);
- }
-
- /**
- * Read titles from a test result file, accepting
- * only those prefixed with [PASS|FAIL] and
- * excluding the "[PASS|FAIL] Suite.Spec(.." entry.
- * @param titlesFile the File containing a
- * list of titles from test results,
- * with some lines of the form
- * <code>[PASS|FAIL] {title}()<code> (excluding
- * <code>[PASS|FAIL] Suite.Spec(...<code>.
- * @param titlesFile the File path to the file containing titles
- * @param fail if true, only select titles prefixed "FAIL"
- * @return the unmodifiable List of titles (maybe empty, never null)
- */
- private static List<String> readTitlesFile(File titlesFile, boolean fail) {
- ArrayList<String> result = new ArrayList<String>();
- Reader reader = null;
- try {
- reader = new FileReader(titlesFile);
- BufferedReader lines = new BufferedReader(reader);
- String line;
- while (null != (line = lines.readLine())) {
- if ((line.startsWith("FAIL ")
- || (!fail && line.startsWith("PASS ")))
- && (!line.substring(5).startsWith("Suite.Spec("))) {
- String title = line.substring(5);
- int loc = title.lastIndexOf("(");
- if (-1 != loc) {
- title = title.substring(0, loc);
- }
- result.add(title);
- }
- }
- } catch (IOException e) {
- System.err.println("ignoring titles in " + titlesFile); // XXX messages
- e.printStackTrace(System.err);
- } finally {
- if (null != reader) {
- try {
- reader.close();
- } catch (IOException e) {
- // ignore
- }
- }
- }
- return Collections.unmodifiableList(result);
- }
-
- /** base directory of the test suite - set before making run */
- private File suiteDir;
-
- /** path offset from suite directory to base of test directory */
- String testDirOffset; // XXX revert to private after fixes
-
- /** id of bug - if 0, then no bug associated with this test */
- private int bugId;
-
- public Spec() {
- super(XMLNAME);
- setXMLNames(NAMES);
- }
-
- protected void initClone(Spec spec)
- throws CloneNotSupportedException {
- super.initClone(spec);
- spec.bugId = bugId;
- spec.suiteDir = suiteDir;
- spec.testDirOffset = testDirOffset;
- }
-
- public Object clone() throws CloneNotSupportedException {
- Spec result = new Spec();
- initClone(result);
- return result;
- }
-
- public void setSuiteDir(File suiteDir) {
- this.suiteDir = suiteDir;
- }
-
- public File getSuiteDir() {
- return suiteDir;
- }
-
- /** @param bugId 100..999999 */
- public void setBugId(int bugId) {
- LangUtil.throwIaxIfFalse((bugId > 10) && (bugId < 1000000), "bad bug id: " + bugId);
- this.bugId = bugId;
- }
-
- public int getBugId() {
- return bugId;
- }
-
- public void setTestDirOffset(String testDirOffset) {
- if (!LangUtil.isEmpty(testDirOffset)) {
- this.testDirOffset = testDirOffset;
- }
- }
-
- public String getTestDirOffset() {
- return (null == testDirOffset ? "" : testDirOffset);
- }
-
- /**
- * @param sandbox ignored
- * @see org.aspectj.testing.harness.bridge.AbstractRunSpec#makeAjcRun(Sandbox, Validator)
- */
- public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator) {
- LangUtil.throwIaxIfNull(validator, "validator");
-
- // if no one set suiteDir, see if we have a source location
- if (null == suiteDir) {
- ISourceLocation loc = getSourceLocation();
- if (!validator.nullcheck(loc, "suite file location")
- || !validator.nullcheck(loc.getSourceFile(), "suite file")) {
- return null;
- }
- File locDir = loc.getSourceFile().getParentFile();
- if (!validator.canReadDir(locDir, "source location dir")) {
- return null;
- }
- suiteDir = locDir;
- }
-
- // we make a new sandbox with more state for our subruns, keep that,
- // in order to defer initialization to nextRun()
- File testBaseDir;
- String testDirOffset = getTestDirOffset();
- if (LangUtil.isEmpty(testDirOffset)) {
- testBaseDir = suiteDir;
- } else {
- testBaseDir = new File(suiteDir, testDirOffset);
- if (!validator.canReadDir(testBaseDir, "testBaseDir")) {
- return null;
- }
- }
- Sandbox childSandbox = null;
- try {
- childSandbox = new Sandbox(testBaseDir, validator);
- validator.registerSandbox(childSandbox);
- } catch (IllegalArgumentException e) {
- validator.fail(e.getMessage());
- return null;
- }
- return new AjcTest(this, childSandbox, validator);
- }
-
- /** @see IXmlWritable#writeXml(XMLWriter) */
- public void writeXml(XMLWriter out) {
- out.println("");
- String value = (null == testDirOffset? "" : testDirOffset);
- String attr = XMLWriter.makeAttribute("dir", value);
- if (0 != bugId) {
- attr += " " + XMLWriter.makeAttribute("pr", ""+bugId);
- }
- out.startElement(xmlElementName, attr, false);
- super.writeAttributes(out);
- out.endAttributes();
- super.writeChildren(out);
- out.endElement(xmlElementName);
- }
-
- /**
- * AjcTest overrides this to skip if
- * <ul>
- * <li>the spec has a keyword the parent wants to skip</li>
- * <li>the spec does not have a required keyword</li>
- * <li>the spec does not have a required bugId</li>
- * <li>the spec does not have a required title (description)n</li>
- * </ul>
- * When skipping, this issues a messages as to why skipped.
- * Skip combinations are not guaranteed to work correctly. XXX
- * @return false if this wants to be skipped, true otherwise
- * @throws Error if selected option is not of the form
- * <pre>-ajctest[Require|Skip]Keywords=keyword{,keyword}..</pre>.
- */
- protected boolean doAdoptParentValues(RT parentRuntime, IMessageHandler handler) {
- if (!super.doAdoptParentValues(parentRuntime, handler)) {
- return false;
- }
- runtime.copy(parentRuntime);
-
- String[] globalOptions = runtime.extractOptions(VALID_OPTIONS, true);
- for (String globalOption : globalOptions) {
- String option = globalOption;
- if (!option.startsWith(OPTION_PREFIX)) {
- throw new Error("only expecting " + OPTION_PREFIX + "..: " + option);
- }
- option = option.substring(OPTION_PREFIX.length());
- boolean keywordMustExist = false;
- List<String> permittedTitles = null;
- List<String> permittedTitleStrings = null;
- String havePr = null;
- if (option.startsWith(REQUIRE_KEYWORDS)) {
- option = option.substring(REQUIRE_KEYWORDS.length());
- keywordMustExist = true;
- } else if (option.startsWith(SKIP_KEYWORDS)) {
- option = option.substring(SKIP_KEYWORDS.length());
- } else if (option.startsWith(TITLE_LIST)) {
- option = option.substring(TITLE_LIST.length());
- permittedTitles = getTitles(option);
- } else if (option.startsWith(TITLE_FAIL_LIST)) {
- option = option.substring(TITLE_FAIL_LIST.length());
- permittedTitles = getTitles(option, true);
- } else if (option.startsWith(TITLE_CONTAINS)) {
- option = option.substring(TITLE_CONTAINS.length());
- permittedTitleStrings = getTitles(option);
- } else if (option.startsWith(PICK_PR)) {
- if (0 == bugId) {
- skipMessage(handler, "bugId required, but no bugId for this test");
- return false;
- } else {
- havePr = "" + bugId;
- }
- option = option.substring(PICK_PR.length());
- } else {
- throw new Error("unrecognized suffix: " + globalOption
- + " (expecting: " + OPTION_PREFIX + VALID_SUFFIXES + "...)");
- }
- if (null != permittedTitleStrings) {
- boolean gotHit = false;
- for (Iterator<String> iter = permittedTitleStrings.iterator();
- !gotHit && iter.hasNext();
- ) {
- String substring = (String) iter.next();
- if (this.description.contains(substring)) {
- gotHit = true;
- }
- }
- if (!gotHit) {
- String reason = "title "
- + this.description
- + " does not contain any of "
- + option;
- skipMessage(handler, reason);
- return false;
- }
- } else if (null != permittedTitles) {
- if (!permittedTitles.contains(this.description)) {
- String reason = "titlesList "
- + option
- + " did not contain "
- + this.description;
- skipMessage(handler, reason);
- return false;
- }
- } else {
- // all other options handled as comma-delimited lists
- List<String> specs = LangUtil.commaSplit(option);
- // XXX also throw Error on empty specs...
- for (String spec : specs) {
- if (null != havePr) {
- if (havePr.equals(spec)) { // String.equals()
- havePr = null;
- }
- } else if (keywordMustExist != keywords.contains(spec)) {
- String reason = "keyword " + spec
- + " was " + (keywordMustExist ? "not found" : "found");
- skipMessage(handler, reason);
- return false;
- }
- }
- if (null != havePr) {
- skipMessage(handler, "bugId required, but not matched for this test");
- return false;
- }
- }
- }
- return true;
- }
-
- } // AjcTest.Spec
-
- /**
- * A suite of AjcTest has children for each AjcTest
- * and flows all options down as globals
- */
- public static class Suite extends RunSpecIterator {
- final Spec spec;
-
- /**
- * Count the number of AjcTest in this suite.
- * @param spec
- * @return
- */
- public static int countTests(Suite.Spec spec) {
- return spec.children.size();
- }
-
- public static AjcTest.Spec[] getTests(Suite.Spec spec) {
- if (null == spec) {
- return new AjcTest.Spec[0];
- }
- return (AjcTest.Spec[]) spec.children.toArray(new AjcTest.Spec[0]);
- }
-
- public Suite(Spec spec, Sandbox sandbox, Validator validator) {
- super(spec, sandbox, validator, false);
- this.spec = spec;
- }
-
- /**
- * While being called to make the sandbox for the child,
- * set up the child's suite dir based on ours.
- * @param child must be instanceof AjcTest.Spec
- * @see org.aspectj.testing.harness.bridge.RunSpecIterator#makeSandbox(IRunSpec, Validator)
- * @return super.makeSandbox(child, validator)
- */
- protected Sandbox makeSandbox(
- IRunSpec child,
- Validator validator) {
- if (!(child instanceof AjcTest.Spec)) {
- validator.fail("only expecting AjcTest children");
- return null;
- }
- if (!validator.canReadDir(spec.suiteDir, "spec.suiteDir")) {
- return null;
- }
- ((AjcTest.Spec) child).setSuiteDir(spec.suiteDir);
- return super.makeSandbox(child, validator);
- }
-
- /**
- * A suite spec contains AjcTest children.
- * The suite dir or source location should be set
- * if the tests do not each have a source location
- * with a source file in the suite dir.
- * XXX whether to write out suiteDir in XML?
- */
- public static class Spec extends AbstractRunSpec {
- public static final String XMLNAME = "suite";
- /**
- * do description, do sourceLocation,
- * do keywords, do options, skip paths, do comment,
- * skip staging, skip badInput,
- * skip dirChanges, skip messages and do children
- * (though we do children directly).
- */
- // private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT,
- // null, null, null, null, "", null, "", "", true, true, false);
- File suiteDir;
- public Spec() {
- super(XMLNAME, false); // do not skip this even if children skip
- }
-
- public Object clone() throws CloneNotSupportedException {
- Spec spec = new Spec();
- super.initClone(spec);
- spec.suiteDir = suiteDir;
- return spec;
- }
-
- /** @param suiteDirPath the String path to the base suite dir */
- public void setSuiteDir(String suiteDirPath) {
- if (!LangUtil.isEmpty(suiteDirPath)) {
- this.suiteDir = new File(suiteDirPath);
- }
- }
-
- /** @param suiteDirFile the File for the base suite dir */
- public void setSuiteDirFile(File suiteDir) {
- this.suiteDir = suiteDir;
- }
-
- /** @return suiteDir from any set or source location if set */
- public File getSuiteDirFile() {
- if (null == suiteDir) {
- ISourceLocation loc = getSourceLocation();
- if (null != loc) {
- File sourceFile = loc.getSourceFile();
- if (null != sourceFile) {
- suiteDir = sourceFile.getParentFile();
- }
- }
- }
- return suiteDir;
- }
-
- /**
- * @return
- * @see org.aspectj.testing.harness.bridge.AbstractRunSpec#makeRunIterator(Sandbox, Validator)
- */
- public IRunIterator makeRunIterator(
- Sandbox sandbox,
- Validator validator) {
- return new Suite(this, sandbox, validator);
- }
-
- public String toString() {
- // removed nKids as misleading, since children.size() may change
- //int nKids = children.size();
- //return "Suite.Spec(" + suiteDir + ", " + nKids + " tests)";
- return "Suite.Spec(" + suiteDir + ")";
- }
- }
- }
- }
|