You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AjcTest.java 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. /* *******************************************************************
  2. * Copyright (c) 1999-2001 Xerox Corporation,
  3. * 2002 Palo Alto Research Center, Incorporated (PARC).
  4. * All rights reserved.
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Public License v1.0
  7. * which accompanies this distribution and is available at
  8. * http://www.eclipse.org/legal/epl-v10.html
  9. *
  10. * Contributors:
  11. * Xerox/PARC initial implementation
  12. * ******************************************************************/
  13. package org.aspectj.testing.harness.bridge;
  14. import java.io.BufferedReader;
  15. import java.io.File;
  16. import java.io.FileReader;
  17. import java.io.IOException;
  18. import java.io.Reader;
  19. import java.util.ArrayList;
  20. import java.util.Arrays;
  21. import java.util.Collections;
  22. import java.util.HashMap;
  23. import java.util.Iterator;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.StringTokenizer;
  27. //import org.aspectj.bridge.*;
  28. import org.aspectj.bridge.IMessageHandler;
  29. import org.aspectj.bridge.ISourceLocation;
  30. import org.aspectj.testing.run.IRunIterator;
  31. import org.aspectj.testing.run.IRunStatus;
  32. import org.aspectj.testing.run.Runner;
  33. import org.aspectj.testing.xml.XMLWriter;
  34. import org.aspectj.util.LangUtil;
  35. /**
  36. * An AjcTest has child subruns (compile, [inc-compile|run]*).
  37. * XXX title keys shared between all instances
  38. * (add Thread to key to restrict access?)
  39. */
  40. public class AjcTest extends RunSpecIterator {
  41. /** Unwrap an AjcTest.Spec from an IRunStatus around an AjcTest */
  42. public static Spec unwrapSpec(IRunStatus status) {
  43. if (null != status) {
  44. Object id = status.getIdentifier();
  45. if (id instanceof Runner.IteratorWrapper) {
  46. IRunIterator iter = ((Runner.IteratorWrapper) id).iterator;
  47. if (iter instanceof AjcTest) {
  48. return (Spec) ((AjcTest) iter).spec;
  49. }
  50. }
  51. }
  52. return null;
  53. }
  54. /** Unwrap initial CompilerRun.Spec from an AjcTest.Spec */
  55. public static CompilerRun.Spec unwrapCompilerRunSpec(Spec spec) {
  56. if (null != spec) {
  57. List kids = spec.getChildren();
  58. if (0 < kids.size()) {
  59. Object o = kids.get(0);
  60. if (o instanceof CompilerRun.Spec) {
  61. return (CompilerRun.Spec) o;
  62. }
  63. }
  64. }
  65. return null;
  66. }
  67. /** The spec creates the sandbox, so we use it throughout */
  68. public AjcTest(Spec spec, Sandbox sandbox, Validator validator) {
  69. super(spec, sandbox, validator, true);
  70. }
  71. /**
  72. * Clear the command from the sandbox, to avoid memory leaks.
  73. * @see org.aspectj.testing.harness.bridge.RunSpecIterator#iterationCompleted()
  74. */
  75. public void iterationCompleted() {
  76. super.iterationCompleted();
  77. sandbox.clearCommand(this);
  78. }
  79. /**
  80. * Specification for an ajc test.
  81. * Keyword directives are global/parent options passed, e.g., as
  82. * <pre>-ajctest[Require|Skip]Keywords=keyword{,keyword}..</pre>.
  83. * See VALID_SUFFIXES for complete list.
  84. */
  85. public static class Spec extends AbstractRunSpec {
  86. public static final String XMLNAME = "ajc-test";
  87. /**
  88. * do description as title, do sourceLocation,
  89. * do keywords, do options, skip paths, do comment,
  90. * skip staging, skip badInput,
  91. * skip dirChanges, do messages and do children
  92. * (though we do children directly).
  93. */
  94. private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT,
  95. "title", null, null, null, "", null, "", "", true, false, false);
  96. private static final String OPTION_PREFIX = "-ajctest";
  97. private static final String[] VALID_OPTIONS = new String[] { OPTION_PREFIX };
  98. private static final String TITLE_LIST = "TitleList=";
  99. private static final String TITLE_FAIL_LIST = "TitleFailList=";
  100. private static final String TITLE_CONTAINS= "TitleContains=";
  101. private static final String REQUIRE_KEYWORDS = "RequireKeywords=";
  102. private static final String SKIP_KEYWORDS = "SkipKeywords=";
  103. private static final String PICK_PR = "PR=";
  104. private static final List<String> VALID_SUFFIXES
  105. = Collections.unmodifiableList(Arrays.asList(new String[]
  106. { TITLE_LIST, TITLE_FAIL_LIST, TITLE_CONTAINS,
  107. REQUIRE_KEYWORDS, SKIP_KEYWORDS, PICK_PR }));
  108. /** Map String titlesName to List (String) of titles to accept */
  109. private static final Map<String,List<String>> TITLES = new HashMap<String,List<String>>();
  110. private static List<String> getTitles(String titlesName) {
  111. return getTitles(titlesName, false);
  112. }
  113. private static List<String> getTitles(String titlesName, boolean fail) {
  114. if (LangUtil.isEmpty(titlesName)) {
  115. return Collections.emptyList();
  116. }
  117. List<String> result = (List<String>) TITLES.get(titlesName);
  118. if (null == result) {
  119. result = makeTitlesList(titlesName, fail);
  120. TITLES.put(titlesName, result);
  121. }
  122. return result;
  123. }
  124. /**
  125. * Make titles list per titlesKey, either a path to a file
  126. * containing "[PASS|FAIL] {title}(..)" entries,
  127. * or a comma-delimited list of titles.
  128. * @param titlesKey a String, either a path to a file
  129. * containing "[PASS|FAIL] {title}(..)" entries,
  130. * or a comma-delimited list of titles.
  131. * @param fail if true, only read titles prefixed "FAIL" from files
  132. * @return the unmodifiable List of titles (maybe empty, never null)
  133. */
  134. private static List<String> makeTitlesList(String titlesKey, boolean fail) {
  135. File file = new File(titlesKey);
  136. return file.canRead()
  137. ? readTitlesFile(file, fail)
  138. : parseTitlesList(titlesKey);
  139. }
  140. /**
  141. * Parse list of titles from comma-delmited list
  142. * titlesList, trimming each entry and permitting
  143. * comma to be escaped with '\'.
  144. * @param titlesList a comma-delimited String of titles
  145. * @return the unmodifiable List of titles (maybe empty, never null)
  146. */
  147. private static List<String> parseTitlesList(String titlesList) {
  148. ArrayList<String> result = new ArrayList<String>();
  149. String last = null;
  150. StringTokenizer st = new StringTokenizer(titlesList, ",");
  151. while (st.hasMoreTokens()) {
  152. String next = st.nextToken().trim();
  153. if (next.endsWith("\\")) {
  154. next = next.substring(0, next.length()-1);
  155. if (null == last) {
  156. last = next;
  157. } else {
  158. last += next;
  159. }
  160. next = null;
  161. } else if (null != last) {
  162. next = (last + next).trim();
  163. last = null;
  164. } else {
  165. next = next.trim();
  166. }
  167. if (!LangUtil.isEmpty(next)) {
  168. result.add(next);
  169. }
  170. }
  171. if (null != last) {
  172. String m = "unterminated entry \"" + last; // XXX messages
  173. System.err.println(m + "\" in " + titlesList);
  174. result.add(last.trim());
  175. }
  176. return Collections.unmodifiableList(result);
  177. }
  178. /**
  179. * Read titles from a test result file, accepting
  180. * only those prefixed with [PASS|FAIL] and
  181. * excluding the "[PASS|FAIL] Suite.Spec(.." entry.
  182. * @param titlesFile the File containing a
  183. * list of titles from test results,
  184. * with some lines of the form
  185. * <code>[PASS|FAIL] {title}()<code> (excluding
  186. * <code>[PASS|FAIL] Suite.Spec(...<code>.
  187. * @param titlesFile the File path to the file containing titles
  188. * @param fail if true, only select titles prefixed "FAIL"
  189. * @return the unmodifiable List of titles (maybe empty, never null)
  190. */
  191. private static List<String> readTitlesFile(File titlesFile, boolean fail) {
  192. ArrayList<String> result = new ArrayList<String>();
  193. Reader reader = null;
  194. try {
  195. reader = new FileReader(titlesFile);
  196. BufferedReader lines = new BufferedReader(reader);
  197. String line;
  198. while (null != (line = lines.readLine())) {
  199. if ((line.startsWith("FAIL ")
  200. || (!fail && line.startsWith("PASS ")))
  201. && (!line.substring(5).startsWith("Suite.Spec("))) {
  202. String title = line.substring(5);
  203. int loc = title.lastIndexOf("(");
  204. if (-1 != loc) {
  205. title = title.substring(0, loc);
  206. }
  207. result.add(title);
  208. }
  209. }
  210. } catch (IOException e) {
  211. System.err.println("ignoring titles in " + titlesFile); // XXX messages
  212. e.printStackTrace(System.err);
  213. } finally {
  214. if (null != reader) {
  215. try {
  216. reader.close();
  217. } catch (IOException e) {
  218. // ignore
  219. }
  220. }
  221. }
  222. return Collections.unmodifiableList(result);
  223. }
  224. /** base directory of the test suite - set before making run */
  225. private File suiteDir;
  226. /** path offset from suite directory to base of test directory */
  227. String testDirOffset; // XXX revert to private after fixes
  228. /** id of bug - if 0, then no bug associated with this test */
  229. private int bugId;
  230. public Spec() {
  231. super(XMLNAME);
  232. setXMLNames(NAMES);
  233. }
  234. protected void initClone(Spec spec)
  235. throws CloneNotSupportedException {
  236. super.initClone(spec);
  237. spec.bugId = bugId;
  238. spec.suiteDir = suiteDir;
  239. spec.testDirOffset = testDirOffset;
  240. }
  241. public Object clone() throws CloneNotSupportedException {
  242. Spec result = new Spec();
  243. initClone(result);
  244. return result;
  245. }
  246. public void setSuiteDir(File suiteDir) {
  247. this.suiteDir = suiteDir;
  248. }
  249. public File getSuiteDir() {
  250. return suiteDir;
  251. }
  252. /** @param bugId 100..999999 */
  253. public void setBugId(int bugId) {
  254. LangUtil.throwIaxIfFalse((bugId > 10) && (bugId < 1000000), "bad bug id: " + bugId);
  255. this.bugId = bugId;
  256. }
  257. public int getBugId() {
  258. return bugId;
  259. }
  260. public void setTestDirOffset(String testDirOffset) {
  261. if (!LangUtil.isEmpty(testDirOffset)) {
  262. this.testDirOffset = testDirOffset;
  263. }
  264. }
  265. public String getTestDirOffset() {
  266. return (null == testDirOffset ? "" : testDirOffset);
  267. }
  268. /**
  269. * @param sandbox ignored
  270. * @see org.aspectj.testing.harness.bridge.AbstractRunSpec#makeAjcRun(Sandbox, Validator)
  271. */
  272. public IRunIterator makeRunIterator(Sandbox sandbox, Validator validator) {
  273. LangUtil.throwIaxIfNull(validator, "validator");
  274. // if no one set suiteDir, see if we have a source location
  275. if (null == suiteDir) {
  276. ISourceLocation loc = getSourceLocation();
  277. if (!validator.nullcheck(loc, "suite file location")
  278. || !validator.nullcheck(loc.getSourceFile(), "suite file")) {
  279. return null;
  280. }
  281. File locDir = loc.getSourceFile().getParentFile();
  282. if (!validator.canReadDir(locDir, "source location dir")) {
  283. return null;
  284. }
  285. suiteDir = locDir;
  286. }
  287. // we make a new sandbox with more state for our subruns, keep that,
  288. // in order to defer initialization to nextRun()
  289. File testBaseDir;
  290. String testDirOffset = getTestDirOffset();
  291. if (LangUtil.isEmpty(testDirOffset)) {
  292. testBaseDir = suiteDir;
  293. } else {
  294. testBaseDir = new File(suiteDir, testDirOffset);
  295. if (!validator.canReadDir(testBaseDir, "testBaseDir")) {
  296. return null;
  297. }
  298. }
  299. Sandbox childSandbox = null;
  300. try {
  301. childSandbox = new Sandbox(testBaseDir, validator);
  302. validator.registerSandbox(childSandbox);
  303. } catch (IllegalArgumentException e) {
  304. validator.fail(e.getMessage());
  305. return null;
  306. }
  307. return new AjcTest(this, childSandbox, validator);
  308. }
  309. /** @see IXmlWritable#writeXml(XMLWriter) */
  310. public void writeXml(XMLWriter out) {
  311. out.println("");
  312. String value = (null == testDirOffset? "" : testDirOffset);
  313. String attr = XMLWriter.makeAttribute("dir", value);
  314. if (0 != bugId) {
  315. attr += " " + XMLWriter.makeAttribute("pr", ""+bugId);
  316. }
  317. out.startElement(xmlElementName, attr, false);
  318. super.writeAttributes(out);
  319. out.endAttributes();
  320. super.writeChildren(out);
  321. out.endElement(xmlElementName);
  322. }
  323. /**
  324. * AjcTest overrides this to skip if
  325. * <ul>
  326. * <li>the spec has a keyword the parent wants to skip</li>
  327. * <li>the spec does not have a required keyword</li>
  328. * <li>the spec does not have a required bugId</li>
  329. * <li>the spec does not have a required title (description)n</li>
  330. * </ul>
  331. * When skipping, this issues a messages as to why skipped.
  332. * Skip combinations are not guaranteed to work correctly. XXX
  333. * @return false if this wants to be skipped, true otherwise
  334. * @throws Error if selected option is not of the form
  335. * <pre>-ajctest[Require|Skip]Keywords=keyword{,keyword}..</pre>.
  336. */
  337. protected boolean doAdoptParentValues(RT parentRuntime, IMessageHandler handler) {
  338. if (!super.doAdoptParentValues(parentRuntime, handler)) {
  339. return false;
  340. }
  341. runtime.copy(parentRuntime);
  342. String[] globalOptions = runtime.extractOptions(VALID_OPTIONS, true);
  343. for (String globalOption : globalOptions) {
  344. String option = globalOption;
  345. if (!option.startsWith(OPTION_PREFIX)) {
  346. throw new Error("only expecting " + OPTION_PREFIX + "..: " + option);
  347. }
  348. option = option.substring(OPTION_PREFIX.length());
  349. boolean keywordMustExist = false;
  350. List<String> permittedTitles = null;
  351. List<String> permittedTitleStrings = null;
  352. String havePr = null;
  353. if (option.startsWith(REQUIRE_KEYWORDS)) {
  354. option = option.substring(REQUIRE_KEYWORDS.length());
  355. keywordMustExist = true;
  356. } else if (option.startsWith(SKIP_KEYWORDS)) {
  357. option = option.substring(SKIP_KEYWORDS.length());
  358. } else if (option.startsWith(TITLE_LIST)) {
  359. option = option.substring(TITLE_LIST.length());
  360. permittedTitles = getTitles(option);
  361. } else if (option.startsWith(TITLE_FAIL_LIST)) {
  362. option = option.substring(TITLE_FAIL_LIST.length());
  363. permittedTitles = getTitles(option, true);
  364. } else if (option.startsWith(TITLE_CONTAINS)) {
  365. option = option.substring(TITLE_CONTAINS.length());
  366. permittedTitleStrings = getTitles(option);
  367. } else if (option.startsWith(PICK_PR)) {
  368. if (0 == bugId) {
  369. skipMessage(handler, "bugId required, but no bugId for this test");
  370. return false;
  371. } else {
  372. havePr = "" + bugId;
  373. }
  374. option = option.substring(PICK_PR.length());
  375. } else {
  376. throw new Error("unrecognized suffix: " + globalOption
  377. + " (expecting: " + OPTION_PREFIX + VALID_SUFFIXES + "...)");
  378. }
  379. if (null != permittedTitleStrings) {
  380. boolean gotHit = false;
  381. for (Iterator<String> iter = permittedTitleStrings.iterator();
  382. !gotHit && iter.hasNext();
  383. ) {
  384. String substring = (String) iter.next();
  385. if (this.description.contains(substring)) {
  386. gotHit = true;
  387. }
  388. }
  389. if (!gotHit) {
  390. String reason = "title "
  391. + this.description
  392. + " does not contain any of "
  393. + option;
  394. skipMessage(handler, reason);
  395. return false;
  396. }
  397. } else if (null != permittedTitles) {
  398. if (!permittedTitles.contains(this.description)) {
  399. String reason = "titlesList "
  400. + option
  401. + " did not contain "
  402. + this.description;
  403. skipMessage(handler, reason);
  404. return false;
  405. }
  406. } else {
  407. // all other options handled as comma-delimited lists
  408. List<String> specs = LangUtil.commaSplit(option);
  409. // XXX also throw Error on empty specs...
  410. for (String spec : specs) {
  411. if (null != havePr) {
  412. if (havePr.equals(spec)) { // String.equals()
  413. havePr = null;
  414. }
  415. } else if (keywordMustExist != keywords.contains(spec)) {
  416. String reason = "keyword " + spec
  417. + " was " + (keywordMustExist ? "not found" : "found");
  418. skipMessage(handler, reason);
  419. return false;
  420. }
  421. }
  422. if (null != havePr) {
  423. skipMessage(handler, "bugId required, but not matched for this test");
  424. return false;
  425. }
  426. }
  427. }
  428. return true;
  429. }
  430. } // AjcTest.Spec
  431. /**
  432. * A suite of AjcTest has children for each AjcTest
  433. * and flows all options down as globals
  434. */
  435. public static class Suite extends RunSpecIterator {
  436. final Spec spec;
  437. /**
  438. * Count the number of AjcTest in this suite.
  439. * @param spec
  440. * @return
  441. */
  442. public static int countTests(Suite.Spec spec) {
  443. return spec.children.size();
  444. }
  445. public static AjcTest.Spec[] getTests(Suite.Spec spec) {
  446. if (null == spec) {
  447. return new AjcTest.Spec[0];
  448. }
  449. return (AjcTest.Spec[]) spec.children.toArray(new AjcTest.Spec[0]);
  450. }
  451. public Suite(Spec spec, Sandbox sandbox, Validator validator) {
  452. super(spec, sandbox, validator, false);
  453. this.spec = spec;
  454. }
  455. /**
  456. * While being called to make the sandbox for the child,
  457. * set up the child's suite dir based on ours.
  458. * @param child must be instanceof AjcTest.Spec
  459. * @see org.aspectj.testing.harness.bridge.RunSpecIterator#makeSandbox(IRunSpec, Validator)
  460. * @return super.makeSandbox(child, validator)
  461. */
  462. protected Sandbox makeSandbox(
  463. IRunSpec child,
  464. Validator validator) {
  465. if (!(child instanceof AjcTest.Spec)) {
  466. validator.fail("only expecting AjcTest children");
  467. return null;
  468. }
  469. if (!validator.canReadDir(spec.suiteDir, "spec.suiteDir")) {
  470. return null;
  471. }
  472. ((AjcTest.Spec) child).setSuiteDir(spec.suiteDir);
  473. return super.makeSandbox(child, validator);
  474. }
  475. /**
  476. * A suite spec contains AjcTest children.
  477. * The suite dir or source location should be set
  478. * if the tests do not each have a source location
  479. * with a source file in the suite dir.
  480. * XXX whether to write out suiteDir in XML?
  481. */
  482. public static class Spec extends AbstractRunSpec {
  483. public static final String XMLNAME = "suite";
  484. /**
  485. * do description, do sourceLocation,
  486. * do keywords, do options, skip paths, do comment,
  487. * skip staging, skip badInput,
  488. * skip dirChanges, skip messages and do children
  489. * (though we do children directly).
  490. */
  491. // private static final XMLNames NAMES = new XMLNames(XMLNames.DEFAULT,
  492. // null, null, null, null, "", null, "", "", true, true, false);
  493. File suiteDir;
  494. public Spec() {
  495. super(XMLNAME, false); // do not skip this even if children skip
  496. }
  497. public Object clone() throws CloneNotSupportedException {
  498. Spec spec = new Spec();
  499. super.initClone(spec);
  500. spec.suiteDir = suiteDir;
  501. return spec;
  502. }
  503. /** @param suiteDirPath the String path to the base suite dir */
  504. public void setSuiteDir(String suiteDirPath) {
  505. if (!LangUtil.isEmpty(suiteDirPath)) {
  506. this.suiteDir = new File(suiteDirPath);
  507. }
  508. }
  509. /** @param suiteDirFile the File for the base suite dir */
  510. public void setSuiteDirFile(File suiteDir) {
  511. this.suiteDir = suiteDir;
  512. }
  513. /** @return suiteDir from any set or source location if set */
  514. public File getSuiteDirFile() {
  515. if (null == suiteDir) {
  516. ISourceLocation loc = getSourceLocation();
  517. if (null != loc) {
  518. File sourceFile = loc.getSourceFile();
  519. if (null != sourceFile) {
  520. suiteDir = sourceFile.getParentFile();
  521. }
  522. }
  523. }
  524. return suiteDir;
  525. }
  526. /**
  527. * @return
  528. * @see org.aspectj.testing.harness.bridge.AbstractRunSpec#makeRunIterator(Sandbox, Validator)
  529. */
  530. public IRunIterator makeRunIterator(
  531. Sandbox sandbox,
  532. Validator validator) {
  533. return new Suite(this, sandbox, validator);
  534. }
  535. public String toString() {
  536. // removed nKids as misleading, since children.size() may change
  537. //int nKids = children.size();
  538. //return "Suite.Spec(" + suiteDir + ", " + nKids + " tests)";
  539. return "Suite.Spec(" + suiteDir + ")";
  540. }
  541. }
  542. }
  543. }