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.

Harness.java 47KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC),
  3. * 2003 Contributors.
  4. * All rights reserved.
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Public License v 2.0
  7. * which accompanies this distribution and is available at
  8. * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.txt
  9. *
  10. * Contributors:
  11. * Xerox/PARC initial implementation
  12. * Wes Isberg 2003 changes.
  13. * ******************************************************************/
  14. package org.aspectj.testing.drivers;
  15. import java.io.BufferedReader;
  16. import java.io.File;
  17. import java.io.FileReader;
  18. import java.io.IOException;
  19. import java.io.PrintStream;
  20. import java.io.PrintWriter;
  21. import java.text.DecimalFormat;
  22. import java.text.SimpleDateFormat;
  23. import java.util.ArrayList;
  24. import java.util.Arrays;
  25. import java.util.Collection;
  26. import java.util.Collections;
  27. import java.util.Date;
  28. import java.util.HashMap;
  29. import java.util.LinkedList;
  30. import java.util.List;
  31. import java.util.Map;
  32. import java.util.Map.Entry;
  33. import java.util.Properties;
  34. import java.util.Set;
  35. import java.util.StringTokenizer;
  36. import org.aspectj.bridge.IMessage;
  37. import org.aspectj.bridge.IMessageHolder;
  38. import org.aspectj.bridge.MessageHandler;
  39. import org.aspectj.bridge.MessageUtil;
  40. import org.aspectj.testing.harness.bridge.AbstractRunSpec;
  41. import org.aspectj.testing.harness.bridge.AjcTest;
  42. import org.aspectj.testing.harness.bridge.CompilerRun;
  43. import org.aspectj.testing.harness.bridge.FlatSuiteReader;
  44. import org.aspectj.testing.harness.bridge.IRunSpec;
  45. import org.aspectj.testing.harness.bridge.IncCompilerRun;
  46. import org.aspectj.testing.harness.bridge.JavaRun;
  47. import org.aspectj.testing.harness.bridge.RunSpecIterator;
  48. import org.aspectj.testing.harness.bridge.Sandbox;
  49. import org.aspectj.testing.harness.bridge.Validator;
  50. import org.aspectj.testing.run.IRun;
  51. import org.aspectj.testing.run.IRunIterator;
  52. import org.aspectj.testing.run.IRunListener;
  53. import org.aspectj.testing.run.IRunStatus;
  54. import org.aspectj.testing.run.IRunValidator;
  55. import org.aspectj.testing.run.RunListener;
  56. import org.aspectj.testing.run.RunStatus;
  57. import org.aspectj.testing.run.RunValidator;
  58. import org.aspectj.testing.run.Runner;
  59. import org.aspectj.testing.util.BridgeUtil;
  60. import org.aspectj.testing.util.RunUtils;
  61. import org.aspectj.testing.util.StreamsHandler;
  62. import org.aspectj.testing.util.StreamsHandler.Result;
  63. import org.aspectj.testing.xml.AjcSpecXmlReader;
  64. import org.aspectj.testing.xml.XMLWriter;
  65. import org.aspectj.util.FileUtil;
  66. import org.aspectj.util.LangUtil;
  67. /**
  68. * Test harness for running AjcTest.Suite test suites.
  69. * This can be easily extended by subclassing.
  70. * <ul>
  71. * <li>template algorithms for reading arguments, printing syntax,
  72. * reading suites, and reporting results all
  73. * delegate to methods that subclasses can override to support
  74. * additional arguments or different reporting.</li>
  75. * <li>implement arbitrary features as IRunListeners</li>
  76. * <li>support single-option aliases to any number of single-options </li>
  77. * </ul>
  78. * See {@link report(IRunStatus, int, int)} for an explanation of test result
  79. * categories.
  80. */
  81. public class Harness {
  82. /**
  83. * Spaces up to the width that an option should take in the syntax,
  84. * including the two-space leader
  85. */
  86. protected static final String SYNTAX_PAD = " ";
  87. protected static final String OPTION_DELIM = ";";
  88. private static final String JAVA_VERSION;
  89. private static final String ASPECTJ_VERSION;
  90. static {
  91. String version = "UNKNOWN";
  92. try { version = System.getProperty("java.version", "UNKNOWN"); }
  93. catch (Throwable t) {}
  94. JAVA_VERSION = version;
  95. version = "UNKNOWN";
  96. try {
  97. Class c = Class.forName("org.aspectj.bridge.Version");
  98. version = (String) c.getField("text").get(null);
  99. } catch (Throwable t) {
  100. // ignore
  101. }
  102. ASPECTJ_VERSION = version;
  103. }
  104. /** factory for the subclass currently anointed as default */
  105. public static Harness makeHarness() {
  106. return new FeatureHarness();
  107. }
  108. /** @param args String[] like runMain(String[]) args */
  109. public static void main(String[] args) throws Exception {
  110. if (LangUtil.isEmpty(args)) {
  111. File argFile = new File("HarnessArgs.txt");
  112. if (argFile.canRead()) {
  113. args = readArgs(argFile);
  114. } else {
  115. args = new String[] { "-help" };
  116. }
  117. }
  118. makeHarness().runMain(args, null);
  119. }
  120. /**
  121. * Get known option aliases.
  122. * Subclasses may add new aliases, where the key is the alias option,
  123. * and the value is a comma-delimited String of target options.
  124. * @return Properties with feature aliases or null
  125. */
  126. protected static Properties getOptionAliases() {
  127. if (null == optionAliases) {
  128. optionAliases = new Properties();
  129. // XXX load from **OptionAliases.properties
  130. }
  131. return optionAliases;
  132. }
  133. /**
  134. * Read argFile contents into String[],
  135. * delimiting at any whitespace
  136. */
  137. private static String[] readArgs(File argFile) {
  138. ArrayList<String> args = new ArrayList<>();
  139. // int lineNum = 0;
  140. try {
  141. BufferedReader stream =
  142. new BufferedReader(new FileReader(argFile));
  143. String line;
  144. while (null != (line = stream.readLine())) {
  145. StringTokenizer st = new StringTokenizer(line);
  146. while (st.hasMoreTokens()) {
  147. args.add(st.nextToken());
  148. }
  149. }
  150. } catch (IOException e) {
  151. e.printStackTrace(System.err);
  152. }
  153. return args.toArray(new String[0]);
  154. }
  155. /** aliases key="option" value="option{,option}" */
  156. private static Properties optionAliases;
  157. /** be extra noisy if true */
  158. private boolean verboseHarness;
  159. /** be extra quiet if true */
  160. private boolean quietHarness;
  161. /** just don't say anything! */
  162. protected boolean silentHarness;
  163. private Map<String,Feature> features;
  164. /** if true, do not delete temporary files. */
  165. private boolean keepTemp;
  166. /** if true, delete temporary files as each test completes. */
  167. private boolean killTemp;
  168. /** if true, then log results in report(..) when done */
  169. private boolean logResults;
  170. /** if true and there were failures, do System.exit({numFailures})*/
  171. private boolean exitOnFailure;
  172. protected Harness() {
  173. features = new HashMap<>();
  174. }
  175. /**
  176. * Entry point for a test.
  177. * This reads in the arguments,
  178. * creates the test suite(s) from the input file(s),
  179. * and for each suite does setup, run, report, and cleanup.
  180. * When arguments are read, any option ending with "-" causes
  181. * option variants, a set of args with and another without the
  182. * option. See {@link LangUtil.optionVariants(String[])} for
  183. * more details.
  184. * @param args the String[] for the test suite - use -help to get options,
  185. * and use "-" suffixes for variants.
  186. * @param resultList List for IRunStatus results - ignored if null
  187. */
  188. public void runMain(String[] args, List resultList) {
  189. LangUtil.throwIaxIfFalse(!LangUtil.isEmpty(args), "empty args");
  190. // read arguments
  191. final ArrayList<String> globals = new ArrayList<>();
  192. final List<String> files = new ArrayList<>();
  193. final LinkedList<String> argList = new LinkedList<>(Arrays.asList(args));
  194. for (int i = 0; i < argList.size(); i++) {
  195. String arg = argList.get(i);
  196. List<String> aliases = aliasOptions(arg);
  197. if (!LangUtil.isEmpty(aliases)) {
  198. argList.remove(i);
  199. argList.addAll(i, aliases);
  200. i--;
  201. continue;
  202. }
  203. if ("-help".equals(arg)) {
  204. logln("java " + Harness.class.getName() + " {option|suiteFile}..");
  205. printSyntax(getLogStream());
  206. return;
  207. } else if (isSuiteFile(arg)) {
  208. files.add(arg);
  209. } else if (!acceptOption(arg)) {
  210. globals.add(arg);
  211. } // else our options absorbed
  212. }
  213. if (0 == files.size()) {
  214. logln("## Error reading arguments: at least 1 suite file required");
  215. logln("java " + Harness.class.getName() + " {option|suiteFile}..");
  216. printSyntax(getLogStream());
  217. return;
  218. }
  219. String[] globalOptions = globals.toArray(new String[0]);
  220. String[][] globalOptionVariants = optionVariants(globalOptions);
  221. AbstractRunSpec.RT runtime = new AbstractRunSpec.RT();
  222. if (verboseHarness) {
  223. runtime.setVerbose(true);
  224. }
  225. // run suites read from each file
  226. AjcTest.Suite.Spec spec;
  227. for (String string : files) {
  228. File suiteFile = new File(string);
  229. if (!suiteFile.canRead()) {
  230. logln("runMain(..) cannot read file: " + suiteFile);
  231. continue;
  232. }
  233. if (null == (spec = readSuite(suiteFile))) {
  234. logln("runMain(..) cannot read suite from file: " + suiteFile);
  235. continue;
  236. }
  237. MessageHandler holder = new MessageHandler();
  238. for (String[] globalOptionVariant : globalOptionVariants) {
  239. runtime.setOptions(globalOptionVariant);
  240. holder.init();
  241. boolean skip = !spec.adoptParentValues(runtime, holder);
  242. // awful/brittle assumption about number of skips == number of skip messages
  243. final List<IMessage> skipList = MessageUtil.getMessages(holder, IMessage.INFO, false, "skip");
  244. if ((verboseHarness || skip || (0 < skipList.size()))) {
  245. final List<String> curArgs = Arrays.asList(globalOptionVariant);
  246. logln("runMain(" + suiteFile + ", " + curArgs + ")");
  247. if (verboseHarness) {
  248. String format = "yyyy.MM.dd G 'at' hh:mm:ss a zzz";
  249. SimpleDateFormat formatter = new SimpleDateFormat (format);
  250. String date = formatter.format(new Date());
  251. logln("test date: " + date);
  252. logln("harness features: " + listFeatureNames());
  253. logln("Java version: " + JAVA_VERSION);
  254. logln("AspectJ version: " + ASPECTJ_VERSION);
  255. }
  256. if (!(quietHarness || silentHarness) && holder.hasAnyMessage(null, true)) {
  257. MessageUtil.print(getLogStream(), holder, "skip - ");
  258. MessageUtil.printMessageCounts(getLogStream(), holder, "skip - ");
  259. }
  260. }
  261. if (!skip) {
  262. doStartSuite(suiteFile);
  263. long elapsed = 0;
  264. RunResult result = null;
  265. try {
  266. final long startTime = System.currentTimeMillis();
  267. result = run(spec);
  268. if (null != resultList) {
  269. resultList.add(result);
  270. }
  271. elapsed = System.currentTimeMillis() - startTime;
  272. report(result.status, skipList.size(), result.numIncomplete, elapsed);
  273. } finally {
  274. doEndSuite(suiteFile,elapsed);
  275. }
  276. if (exitOnFailure) {
  277. int numFailures = RunUtils.numFailures(result.status, true);
  278. if (0 < numFailures) {
  279. System.exit(numFailures);
  280. }
  281. Object value = result.status.getResult();
  282. if ((value instanceof Boolean)
  283. && !(Boolean) value) {
  284. System.exit(-1);
  285. }
  286. }
  287. }
  288. }
  289. }
  290. }
  291. /**
  292. * Tell all IRunListeners that we are about to start a test suite
  293. * @param suiteFile
  294. * @param elapsed
  295. */
  296. private void doEndSuite(File suiteFile, long elapsed) {
  297. Collection c = features.values();
  298. for (Object o : c) {
  299. Feature element = (Feature) o;
  300. if (element.listener instanceof TestCompleteListener) {
  301. ((TestCompleteListener) element.listener).doEndSuite(suiteFile, elapsed);
  302. }
  303. }
  304. }
  305. /**
  306. * Generate variants of String[] options by creating an extra set for
  307. * each option that ends with "-". If none end with "-", then an
  308. * array equal to <code>new String[][] { options }</code> is returned;
  309. * if one ends with "-", then two sets are returned,
  310. * three causes eight sets, etc.
  311. * @return String[][] with each option set.
  312. * @throws IllegalArgumentException if any option is null or empty.
  313. */
  314. public static String[][] optionVariants(String[] options) {
  315. if ((null == options) || (0 == options.length)) {
  316. return new String[][] { new String[0]};
  317. }
  318. // be nice, don't stomp input
  319. String[] temp = new String[options.length];
  320. System.arraycopy(options, 0, temp, 0, temp.length);
  321. options = temp;
  322. boolean[] dup = new boolean[options.length];
  323. int numDups = 0;
  324. for (int i = 0; i < options.length; i++) {
  325. String option = options[i];
  326. if (LangUtil.isEmpty(option)) {
  327. throw new IllegalArgumentException("empty option at " + i);
  328. }
  329. if (option.endsWith("-")) {
  330. options[i] = option.substring(0, option.length()-1);
  331. dup[i] = true;
  332. numDups++;
  333. }
  334. }
  335. final String[] NONE = new String[0];
  336. final int variants = exp(2, numDups);
  337. final String[][] result = new String[variants][];
  338. // variant is a bitmap wrt doing extra value when dup[k]=true
  339. for (int variant = 0; variant < variants; variant++) {
  340. ArrayList<String> next = new ArrayList<>();
  341. int nextOption = 0;
  342. for (int k = 0; k < options.length; k++) {
  343. if (!dup[k] || (0 != (variant & (1 << (nextOption++))))) {
  344. next.add(options[k]);
  345. }
  346. }
  347. result[variant] = next.toArray(NONE);
  348. }
  349. return result;
  350. }
  351. private static int exp(int base, int power) { // not in Math?
  352. if (0 > power) {
  353. throw new IllegalArgumentException("negative power: " + power);
  354. }
  355. int result = 1;
  356. while (0 < power--) {
  357. result *= base;
  358. }
  359. return result;
  360. }
  361. /**
  362. * @param suiteFile
  363. */
  364. private void doStartSuite(File suiteFile) {
  365. Collection<Feature> c = features.values();
  366. for (Feature element : c) {
  367. if (element.listener instanceof TestCompleteListener) {
  368. ((TestCompleteListener)element.listener).doStartSuite(suiteFile);
  369. }
  370. }
  371. }
  372. /** Run the test suite specified by the spec */
  373. protected RunResult run(AjcTest.Suite.Spec spec) {
  374. LangUtil.throwIaxIfNull(spec, "spec");
  375. /*
  376. * For each run, initialize the runner and validator,
  377. * create a new set of IRun{Iterator} tests,
  378. * and run them.
  379. * Delete all temp files when done.
  380. */
  381. Runner runner = new Runner();
  382. if (0 != features.size()) {
  383. for (Entry<String, Feature> entry : features.entrySet()) {
  384. Feature feature = entry.getValue();
  385. runner.registerListener(feature.clazz, feature.listener);
  386. }
  387. }
  388. IMessageHolder holder = new MessageHandler();
  389. int numIncomplete = 0;
  390. RunStatus status = new RunStatus(holder, runner);
  391. status.setIdentifier(spec);
  392. // validator is used for all setup in entire tree...
  393. Validator validator = new Validator(status);
  394. if (!killTemp) {
  395. validator.lock(this);
  396. }
  397. Sandbox sandbox = null;
  398. try {
  399. sandbox = new Sandbox(spec.getSuiteDirFile(), validator);
  400. IRunIterator tests = spec.makeRunIterator(sandbox, validator);
  401. runner.runIterator(tests, status, null);
  402. if (tests instanceof RunSpecIterator) {
  403. numIncomplete = ((RunSpecIterator) tests).getNumIncomplete();
  404. }
  405. } finally {
  406. if (!keepTemp) {
  407. if (!killTemp) {
  408. validator.unlock(this);
  409. }
  410. validator.deleteTempFiles(verboseHarness);
  411. }
  412. }
  413. return new RunResult(status, numIncomplete);
  414. }
  415. /**
  416. * Report the results of a test run after it is completed.
  417. * Clients should be able to identify the number of:
  418. * <ul>
  419. * <li>tests run and passed</li>
  420. * <li>tests failed, i.e., run and not passed (fail, error, etc.)</li>
  421. * <li>tests incomplete, i.e., test definition read but test run setup failed</li>
  422. * <li>tests skipped, i.e., test definition read and found incompatible with
  423. * the current configuration.</li>
  424. * <ul>
  425. *
  426. * @param status returned from the run
  427. * @param numSkipped int tests that were skipped because of
  428. * configuration incompatibilities
  429. * @param numIncomplete int tests that failed during setup,
  430. * usually indicating a test definition or configuration error.
  431. * @param msElapsed elapsed time in milliseconds
  432. * */
  433. protected void report(IRunStatus status, int numSkipped, int numIncomplete,
  434. long msElapsed ) {
  435. if (logResults) {
  436. RunUtils.AJCSUITE_PRINTER.printRunStatus(getLogStream(), status);
  437. } else if (!(quietHarness || silentHarness) && (0 < status.numMessages(null, true))) {
  438. if (!silentHarness) {
  439. MessageUtil.print(getLogStream(), status, "");
  440. }
  441. }
  442. logln(BridgeUtil.childString(status, numSkipped, numIncomplete)
  443. + " " + (msElapsed/1000) + " seconds");
  444. }
  445. // --------------- delegate methods
  446. protected void logln(String s) {
  447. if (!silentHarness) {
  448. getLogStream().println(s);
  449. }
  450. }
  451. protected PrintStream getLogStream() {
  452. return System.out;
  453. }
  454. protected boolean isSuiteFile(String arg) {
  455. return ((null != arg)
  456. && (arg.endsWith(".txt") || arg.endsWith(".xml"))
  457. && new File(arg).canRead());
  458. }
  459. /**
  460. * Get the options that the input option is an alias for.
  461. * Subclasses may add options directly to the getFeatureAliases result
  462. * or override this.
  463. * @return null if the input is not an alias for other options,
  464. * or a non-empty List (String) of options that this option is an alias for
  465. */
  466. protected List<String> aliasOptions(String option) {
  467. Properties aliases = Harness.getOptionAliases();
  468. if (null != aliases) {
  469. String args = aliases.getProperty(option);
  470. if (!LangUtil.isEmpty(args)) {
  471. return LangUtil.anySplit(args, OPTION_DELIM);
  472. }
  473. }
  474. return null;
  475. }
  476. /**
  477. * Read and implement any of our options.
  478. * Options other than this and suite files will be
  479. * passed down as parent options through the test spec hierarchy.
  480. * Subclasses override this to implement new options.
  481. */
  482. protected boolean acceptOption(String option) {
  483. // boolean result = false;
  484. if (LangUtil.isEmpty(option)) {
  485. return true; // skip bad input
  486. } else if ("-verboseHarness".equals(option)) {
  487. verboseHarness = true;
  488. } else if ("-quietHarness".equals(option)) {
  489. quietHarness = true;
  490. } else if ("-silentHarness".equals(option)) {
  491. silentHarness = true;
  492. } else if ("-keepTemp".equals(option)) {
  493. keepTemp = true;
  494. } else if ("-killTemp".equals(option)) {
  495. killTemp = true;
  496. } else if ("-logResults".equals(option)) {
  497. logResults = true;
  498. } else if ("-exitOnFailure".equals(option)) {
  499. exitOnFailure = true;
  500. } else {
  501. return false;
  502. }
  503. return true;
  504. }
  505. /**
  506. * Read a test suite file.
  507. * This implementation knows how to read .txt and .xml files
  508. * and logs any errors.
  509. * Subclasses override this to read new kinds of suites.
  510. * @return null if unable to read (logging errors) or AjcTest.Suite.Spec otherwise
  511. */
  512. protected AjcTest.Suite.Spec readSuite(File suiteFile) {
  513. if (null != suiteFile) {
  514. String path = suiteFile.getPath();
  515. try {
  516. if (path.endsWith(".xml")) {
  517. return AjcSpecXmlReader.getReader().readAjcSuite(suiteFile);
  518. } else if (path.endsWith(".txt")) {
  519. return FlatSuiteReader.ME.readSuite(suiteFile);
  520. } else {
  521. logln("unrecognized extension? " + path);
  522. }
  523. } catch (IOException e) {
  524. e.printStackTrace(getLogStream());
  525. }
  526. }
  527. return null;
  528. }
  529. /** Add feature to take effect during the next runMain(..) invocation.
  530. * @param feature the Feature to add, using feature.name as key.
  531. */
  532. protected void addFeature(Feature feature) {
  533. if (null != feature) {
  534. features.put(feature.name, feature);
  535. }
  536. }
  537. /** remove feature by name (same as feature.name) */
  538. protected void removeFeature(String name) {
  539. if (!LangUtil.isEmpty(name)) {
  540. features.remove(name);
  541. }
  542. }
  543. /** @return unmodifiable Set of feature names */
  544. protected Set listFeatureNames() {
  545. return Collections.unmodifiableSet(features.keySet());
  546. }
  547. /** print detail message for syntax of main(String[]) command-line */
  548. protected void printSyntax(PrintStream out) {
  549. out.println(" {??} unrecognized options are used as test spec globals");
  550. out.println(" -help print this help message");
  551. out.println(" -verboseHarness harness components log verbosely");
  552. out.println(" -quietHarness harness components suppress logging");
  553. out.println(" -keepTemp do not delete temporary files");
  554. out.println(" -logResults log results at end, verbosely if fail");
  555. out.println(" -exitOnFailure do System.exit({num-failures}) if suite fails");
  556. out.println(" {suiteFile}.xml.. specify test suite XML file");
  557. out.println(" {suiteFile}.txt.. specify test suite .txt file (deprecated)");
  558. }
  559. /** print known aliases at the end of the syntax message */
  560. protected void printAliases(PrintStream out) {
  561. LangUtil.throwIaxIfNull(out, "out");
  562. Map props = getOptionAliases();
  563. if (null == props) {
  564. return;
  565. }
  566. int pdLength = SYNTAX_PAD.length();
  567. Set<Map.Entry<Object,Object>> entries = props.entrySet();
  568. for (Map.Entry<Object,Object> entry : entries) {
  569. String alias = " " + (String) entry.getKey();
  570. int buf = pdLength - alias.length();
  571. if (0 < buf) {
  572. alias += SYNTAX_PAD.substring(0, buf);
  573. } else {
  574. alias += " ";
  575. }
  576. out.println(alias + entry.getValue());
  577. }
  578. }
  579. /** result struct for run(AjcTest.Spec) */
  580. public static class RunResult {
  581. public final IRunStatus status;
  582. public final int numIncomplete;
  583. public RunResult(IRunStatus status, int numIncomplete) {
  584. this.status = status;
  585. this.numIncomplete = numIncomplete;
  586. }
  587. }
  588. /** feature implemented as named IRunIterator/IRun association */
  589. public static class Feature {
  590. /** never null, always assignable to IRun */
  591. public final Class clazz;
  592. /** never null */
  593. public final IRunListener listener;
  594. /** never null or empty */
  595. public final String name;
  596. /** @throws IllegalArgumentException if any is null/empty or clazz is
  597. * not assignable to IRun
  598. */
  599. public Feature(String name, Class clazz, IRunListener listener) {
  600. LangUtil.throwIaxIfNull(clazz, "class");
  601. if (!IRun.class.isAssignableFrom(clazz)
  602. && !IRunIterator.class.isAssignableFrom(clazz)) {
  603. String s = clazz.getName() + "is not assignable to IRun or IRunIterator";
  604. LangUtil.throwIaxIfFalse(false, s);
  605. }
  606. LangUtil.throwIaxIfNull(listener, "listener");
  607. LangUtil.throwIaxIfNull(name, "name");
  608. LangUtil.throwIaxIfFalse(0 < name.length(), "empty name");
  609. this.clazz = clazz;
  610. this.listener = listener;
  611. this.name = name;
  612. }
  613. /** @return feature name */
  614. @Override
  615. public String toString() {
  616. return name;
  617. }
  618. }
  619. }
  620. /**
  621. * Harness with features for controlling output
  622. * (logging results and hiding streams).
  623. * Use -help to get a list of feature options.
  624. */
  625. class FeatureHarness extends Harness {
  626. private static final String[] ALIASES = new String[]
  627. { "-hideStreams",
  628. "-hideCompilerStreams"
  629. + OPTION_DELIM + "-hideRunStreams",
  630. "-jim",
  631. "-logMinFail"
  632. + OPTION_DELIM + "-hideStreams",
  633. "-loud",
  634. "-verboseHarness",
  635. "-baseline",
  636. "-verboseHarness"
  637. + OPTION_DELIM + "-traceTestsMin"
  638. + OPTION_DELIM + "-hideStreams",
  639. "-release",
  640. "-baseline"
  641. + OPTION_DELIM + "-ajctestSkipKeywords=knownLimitation,purejava",
  642. "-junit",
  643. "-silentHarness" + OPTION_DELIM + "-logJUnit" + OPTION_DELIM +
  644. "-hideStreams",
  645. "-cruisecontrol",
  646. "-junit" + OPTION_DELIM + "-ajctestSkipKeywords=knownLimitation,purejava"
  647. };
  648. static {
  649. Map optionAliases = Harness.getOptionAliases();
  650. if (null != optionAliases) {
  651. for (int i = 1; i < ALIASES.length; i += 2) {
  652. optionAliases.put(ALIASES[i-1], ALIASES[i]);
  653. }
  654. }
  655. }
  656. /** controller for suppressing and sniffing error and output streams. */
  657. StreamsHandler streamsHandler;
  658. /** facility of hiding-streams may be applied in many features */
  659. IRunListener streamHider;
  660. /** facility of capture/log may be applied in many features */
  661. IRunListener captureLogger;
  662. /** when making tests, do not run them */
  663. TestMaker testMaker;
  664. public FeatureHarness() {
  665. super();
  666. streamsHandler = new StreamsHandler(false, true);
  667. }
  668. /** override to make tests or run as usual */
  669. @Override
  670. protected RunResult run(AjcTest.Suite.Spec spec) {
  671. if (null != testMaker) {
  672. System.out.println("generating rather than running tests...");
  673. return testMaker.run(spec);
  674. } else {
  675. return super.run(spec);
  676. }
  677. }
  678. /**
  679. * Log via StreamsHandler-designated log stream.
  680. * @see org.aspectj.testing.drivers.Harness#log(String)
  681. */
  682. @Override
  683. protected void logln(String s) {
  684. if (!silentHarness)
  685. streamsHandler.lnlog(s);
  686. }
  687. /**
  688. * @see org.aspectj.testing.drivers.Harness#getLogStream()
  689. * @return StreamsHandler-designated log stream.
  690. */
  691. @Override
  692. protected PrintStream getLogStream() {
  693. return streamsHandler.out;
  694. }
  695. /** print detail message for syntax of main(String[]) command-line */
  696. @Override
  697. protected void printSyntax(PrintStream out) {
  698. super.printSyntax(out);
  699. out.println(" -progressDots log . or ! for each AjcTest pass or fail");
  700. out.println(" -logFail log each failed AjcTest");
  701. out.println(" -logPass log each passed AjcTest");
  702. out.println(" -logAll log each AjcTest");
  703. out.println(" -logMinFail log each AjcTest failure with minimal excess data");
  704. out.println(" -logMinPass log each AjcTest success with minimal excess data");
  705. out.println(" -logMinAll log all AjcTest with minimal excess data");
  706. out.println(" -logXMLFail log XML definition for each failed AjcTest");
  707. out.println(" -logXMLPass log XML definition for each passed AjcTest");
  708. out.println(" -logXMLAll log XML definition for each AjcTest");
  709. out.println(" -logJUnit log all tests in JUnit XML report style");
  710. out.println(" -hideRunStreams hide err/out streams during java runs");
  711. out.println(" -hideCompilerStreams hide err/out streams during compiler runs");
  712. out.println(" -traceTests log pass|fail, /time/memory taken after each test");
  713. out.println(" -traceTestsMin log pass|fail after each test");
  714. out.println(" -XmakeTests create source files/dirs for initial compile run of each test");
  715. out.println(" -XlogPublicType log test XML if \"public type\" in an error message");
  716. out.println(" -XlogSourceIn=Y,Z log test XML if Y or Z is in path of any sources");
  717. super.printAliases(out);
  718. }
  719. /** Accept a number of logging and output options */
  720. @Override
  721. protected boolean acceptOption(String option) {
  722. if (null == option) {
  723. return false;
  724. }
  725. final StreamsHandler streams = streamsHandler;
  726. final IRunValidator validator = RunValidator.NORMAL;
  727. final RunUtils.IRunStatusPrinter verbose
  728. = RunUtils.VERBOSE_PRINTER;
  729. final RunUtils.IRunStatusPrinter terse
  730. = RunUtils.TERSE_PRINTER;
  731. // final boolean LOGPASS = true;
  732. // final boolean LOGFAIL = true;
  733. // final boolean SKIPPASS = false;
  734. // final boolean SKIPFAIL = false;
  735. // final boolean LOGSTREAMS = true;
  736. final boolean SKIPSTREAMS = false;
  737. Feature feature = null;
  738. if (super.acceptOption(option)) {
  739. // ok, result returned below
  740. } else if ("-XmakeTests".equals(option)) {
  741. testMaker = TestMaker.ME;
  742. } else if (option.startsWith("-traceTestsMin")) {
  743. feature = new Feature(option, AjcTest.class,new TestTraceLogger(streams, false));
  744. } else if (option.startsWith("-traceTests")) {
  745. feature = new Feature(option, AjcTest.class,new TestTraceLogger(streams, true));
  746. } else if (option.startsWith("-logMin")) {
  747. feature = new Feature(option, AjcTest.class,
  748. new RunLogger(option, SKIPSTREAMS, streams, validator, terse));
  749. } else if (option.startsWith("-logXML")) {
  750. feature = new Feature(option, AjcTest.class,
  751. new XmlLogger(option, streams, validator));
  752. } else if (option.startsWith("-logJUnit")) {
  753. feature = new Feature(option, AjcTest.class,
  754. new JUnitXMLLogger(option,streams,validator));
  755. } else if (option.startsWith("-log")) {
  756. feature = new Feature(option, AjcTest.class,
  757. new RunLogger(option, SKIPSTREAMS, streams, validator, verbose));
  758. } else if ("-hideRunStreams".equals(option)) {
  759. feature = new Feature(option, JavaRun.class, getStreamHider());
  760. } else if ("-hideCompilerStreams".equals(option)) {
  761. addFeature(new Feature(option, IncCompilerRun.class, getStreamHider())); // hmmm
  762. feature = new Feature(option, CompilerRun.class, getStreamHider());
  763. } else if ("-progressDots".equals(option)) {
  764. IRunListener listener = new RunListener() {
  765. @Override
  766. public void runCompleted(IRunStatus run) {
  767. streamsHandler.log((validator.runPassed(run) ? "." : "!"));
  768. }
  769. };
  770. feature = new Feature(option, AjcTest.class, listener);
  771. } else if (option.startsWith("-XlogPublicType")) {
  772. String label = option + TestCompleteListener.PASS; // print when validator true
  773. feature = new Feature(option, AjcTest.class,
  774. new XmlLogger(label, streams, MessageRunValidator.PUBLIC_TYPE_ERROR));
  775. } else if (option.startsWith("-XlogSourceIn")) {
  776. String input = option.substring("-XlogSourceIn=".length());
  777. LangUtil.throwIaxIfFalse(0 < input.length(), option);
  778. String label = "-XlogSourceIn=" + TestCompleteListener.PASS; // print when validator true
  779. StringRunner sr = new SubstringRunner(input, false);
  780. feature = new Feature(option, AjcTest.class,
  781. new XmlLogger(label, streams, new SourcePathValidator(sr)));
  782. } else {
  783. return false;
  784. }
  785. addFeature(feature);
  786. return true;
  787. }
  788. /** lazy construction for shared hider */
  789. protected IRunListener getStreamHider() {
  790. if (null == streamHider) {
  791. streamHider = new RunListener() {
  792. @Override
  793. public void runStarting(IRunStatus run) {
  794. streamsHandler.hide();
  795. }
  796. @Override
  797. public void runCompleted(IRunStatus run) {
  798. streamsHandler.show();
  799. }
  800. @Override
  801. public String toString() { return "Harness StreamHider"; }
  802. };
  803. }
  804. return streamHider;
  805. }
  806. }
  807. /** Generate any needed test case files for any test. */
  808. class TestMaker {
  809. static TestMaker ME = new TestMaker();
  810. /** @throws Error if unable to make dir */
  811. static void mkdirs(File dir) {
  812. if (null != dir && !dir.exists()) {
  813. if (!dir.mkdirs()) {
  814. throw new Error("unable to make dir: " + dir);
  815. }
  816. }
  817. }
  818. static String getFileContents(File baseDir, File file, String label) {
  819. String fileName = file.getName();
  820. if (fileName.endsWith(".java")) {
  821. fileName = fileName.substring(0, fileName.length() - 5);
  822. }
  823. StringBuilder sb = new StringBuilder();
  824. String filePath = file.getParentFile().getAbsolutePath();
  825. String dirPath = baseDir.getAbsolutePath();
  826. String pack = null;
  827. if (filePath.startsWith(dirPath)) {
  828. pack = filePath.substring(dirPath.length()).replace('/', '.');
  829. }
  830. if (!LangUtil.isEmpty(pack)) {
  831. sb.append("package " + pack + ";");
  832. }
  833. final String EOL = "\n"; // XXX find discovered EOL
  834. sb.append( EOL
  835. + EOL + "import org.aspectj.testing.Tester;"
  836. + EOL + ""
  837. + EOL + "/** @testcase " + label + " */"
  838. + EOL + "public class " + fileName + " {"
  839. + EOL + "\tpublic static void main(String[] args) { "
  840. + EOL + "\t\tTester.check(null != args, \"null args\"); "
  841. + EOL + "\t}"
  842. + EOL + "}"
  843. + EOL
  844. );
  845. return sb.toString();
  846. }
  847. /** create a minimal source file for a test */
  848. static void createSrcFile(File baseDir, File file, String testName) {
  849. if (file.exists()) {
  850. return;
  851. }
  852. String contents = getFileContents(baseDir, file, testName);
  853. String error = FileUtil.writeAsString(file, contents);
  854. if (null != error) {
  855. throw new Error(error);
  856. }
  857. }
  858. /** create an empty arg file for a test */
  859. static void createArgFile(File baseDir, File file, String testName) {
  860. if (file.exists()) {
  861. return;
  862. }
  863. String contents = "// argfile " + file;
  864. String error = FileUtil.writeAsString(file, contents);
  865. if (null != error) {
  866. throw new Error(error);
  867. }
  868. }
  869. public Harness.RunResult run(AjcTest.Suite.Spec spec) {
  870. Iterable<IRunSpec> kids = spec.getChildren();
  871. for (IRunSpec iRunSpec : kids) {
  872. makeTest( (AjcTest.Spec) iRunSpec);
  873. }
  874. IRunStatus status = new RunStatus(new MessageHandler(), new Runner());
  875. status.start();
  876. status.finish(IRunStatus.PASS);
  877. return new Harness.RunResult(status, 0);
  878. }
  879. private void makeTest(AjcTest.Spec spec) {
  880. CompilerRun.Spec compileSpec = AjcTest.unwrapCompilerRunSpec(spec);
  881. if (null == spec) {
  882. throw new Error("null spec");
  883. }
  884. System.out.println(" generating test files for test: " + spec.getDescription());
  885. File dir = spec.getSuiteDir();
  886. if (null != dir) {
  887. TestMaker.mkdirs(dir);
  888. }
  889. String offset = spec.getTestDirOffset();
  890. if (!LangUtil.isEmpty(offset)) {
  891. if (null == dir) {
  892. dir = new File(offset);
  893. } else {
  894. dir = new File(dir.getAbsolutePath() + "/" + offset);
  895. }
  896. } else if (null == dir) {
  897. dir = new File(".");
  898. }
  899. StringBuilder testName = new StringBuilder();
  900. int pr = spec.getBugId();
  901. if (0 < pr) {
  902. testName.append("PR#" + pr + " ");
  903. }
  904. testName.append(spec.getDescription());
  905. final String label = testName.toString();
  906. final File[] srcFiles = FileUtil.getBaseDirFiles(dir, compileSpec.getPathsArray());
  907. if (!LangUtil.isEmpty(srcFiles)) {
  908. for (File srcFile : srcFiles) {
  909. TestMaker.createSrcFile(dir, srcFile, label);
  910. }
  911. }
  912. final File[] argFiles = FileUtil.getBaseDirFiles(dir, compileSpec.getArgfilesArray());
  913. if (!LangUtil.isEmpty(argFiles)) {
  914. for (File argFile : argFiles) {
  915. TestMaker.createArgFile(dir, argFile, label);
  916. }
  917. }
  918. }
  919. /** @return "Testmaker()" */
  920. @Override
  921. public String toString() {
  922. return "TestMaker()";
  923. }
  924. }
  925. interface StringRunner {
  926. boolean accept(String s);
  927. }
  928. /**
  929. * StringRunner than accepts input matching 0+ substrings,
  930. * optionally case-insensitive.
  931. */
  932. class SubstringRunner implements StringRunner {
  933. private static String[] extractSubstrings(
  934. String substrings,
  935. boolean caseSensitive) {
  936. if (null == substrings) {
  937. return null;
  938. }
  939. StringTokenizer st = new StringTokenizer(substrings, ",");
  940. String[] result = new String[st.countTokens()];
  941. for (int i = 0; i < result.length; i++) {
  942. result[i] = st.nextToken().trim();
  943. LangUtil.throwIaxIfFalse(0 < result[i].length(), "empty entry");
  944. if (!caseSensitive) {
  945. result[i] = result[i].toLowerCase();
  946. }
  947. }
  948. return result;
  949. }
  950. private final String[] substrings;
  951. private final boolean caseSensitive;
  952. /**
  953. * @param substrings the String containing comma-separated substrings
  954. * to find in input - if null, any input accepted
  955. * @param caseSensitive if true, do case-sensitive comparison
  956. * @throws IllegalArgumentException if any substrings contains empty entry ", ,"
  957. */
  958. SubstringRunner(String substrings, boolean caseSensitive) {
  959. this.caseSensitive = caseSensitive;
  960. this.substrings = extractSubstrings(substrings, caseSensitive);
  961. }
  962. @Override
  963. public boolean accept(String input) {
  964. if (null == substrings) {
  965. return true;
  966. }
  967. if (null == input) {
  968. return false;
  969. }
  970. if (!caseSensitive) {
  971. input = input.toLowerCase();
  972. }
  973. for (String substring : substrings) {
  974. if (input.contains(substring)) {
  975. return true;
  976. }
  977. }
  978. return false;
  979. }
  980. }
  981. /**
  982. * Signal whether run "passed" based on validating absolute source paths.
  983. * (Static evaluation - no run necessary)
  984. */
  985. class SourcePathValidator implements IRunValidator { // static - no run needed
  986. private final StringRunner validator;
  987. // XXX hoist common
  988. SourcePathValidator(StringRunner validator) {
  989. LangUtil.throwIaxIfNull(validator, "validator");
  990. this.validator = validator;
  991. }
  992. /**
  993. * @return true if any source files in compile spec are
  994. * accepted by the validator.
  995. * @see org.aspectj.testing.run.IRunValidator#runPassed(IRunStatus)
  996. */
  997. @Override
  998. public boolean runPassed(IRunStatus run) {
  999. AjcTest.Spec testSpec = AjcTest.unwrapSpec(run);
  1000. if (null != testSpec) {
  1001. CompilerRun.Spec compileSpec = AjcTest.unwrapCompilerRunSpec(testSpec);
  1002. File basedir = new File(testSpec.getSuiteDir(), testSpec.getTestDirOffset());
  1003. String[] paths = compileSpec.getPathsArray();
  1004. File[] files = FileUtil.getBaseDirFiles(basedir, paths);
  1005. for (File file : files) {
  1006. if (validator.accept(file.getAbsolutePath())) {
  1007. return true;
  1008. }
  1009. }
  1010. }
  1011. return false;
  1012. }
  1013. }
  1014. /** Signal whether run "passed" based on message kind and content */
  1015. class MessageRunValidator implements IRunValidator {
  1016. /** signals "passed" if any error contains "public type" */
  1017. static final IRunValidator PUBLIC_TYPE_ERROR
  1018. = new MessageRunValidator("public type", IMessage.ERROR, false);
  1019. private final IMessage.Kind kind;
  1020. private final String sought;
  1021. private final boolean orGreater;
  1022. /**
  1023. * @param sought the String to seek anywhere in any message of the right kind
  1024. * if null, accept any message of the right kind.
  1025. * @param kind the IMessage.Kind of messages to search - all if null
  1026. */
  1027. MessageRunValidator(String sought, IMessage.Kind kind, boolean orGreater) {
  1028. this.sought = sought;
  1029. this.kind = kind;
  1030. this.orGreater = orGreater;
  1031. }
  1032. /** @return true if this run has messages of the right kind and text */
  1033. @Override
  1034. public boolean runPassed(IRunStatus run) {
  1035. return gotMessage(new IRunStatus[] {run});
  1036. }
  1037. /**
  1038. * Search these children and their children recursively
  1039. * for messages of the right kind and content.
  1040. * @return true at first match of message of the right kind and content
  1041. */
  1042. private boolean gotMessage(IRunStatus[] children) {
  1043. if (LangUtil.isEmpty(children)) {
  1044. return false;
  1045. }
  1046. for (IRunStatus run : children) {
  1047. if (null == run) {
  1048. continue; // hmm
  1049. }
  1050. IMessage[] messages = run.getMessages(kind, orGreater);
  1051. if (!LangUtil.isEmpty(messages)) {
  1052. if (LangUtil.isEmpty(sought)) {
  1053. return true;
  1054. } else {
  1055. for (IMessage message : messages) {
  1056. if (null == message) {
  1057. continue; // hmm
  1058. }
  1059. String text = message.getMessage();
  1060. if ((null != text) && (text.contains(sought))) {
  1061. return true;
  1062. }
  1063. }
  1064. }
  1065. }
  1066. if (gotMessage(run.getChildren())) {
  1067. return true;
  1068. }
  1069. }
  1070. return false;
  1071. }
  1072. }
  1073. /**
  1074. * Base class for listeners that run depending on pass/fail status of input.
  1075. * Template method runCompleted handled whether to run.
  1076. * Subclasses implement doRunCompleted(..).
  1077. */
  1078. abstract class TestCompleteListener extends RunListener {
  1079. /** label suffix indicating both pass and fail */
  1080. public static final String ALL = "All";
  1081. /** label suffix indicating fail */
  1082. public static final String FAIL = "Fail";
  1083. /** label suffix indicating pass */
  1084. public static final String PASS = "Pass";
  1085. /** runValidator determines if a given run passed */
  1086. protected final IRunValidator runValidator;
  1087. /** label for this listener */
  1088. final String label;
  1089. /** if trun and run passed, then run doRunCompleted(..) */
  1090. final boolean logOnPass;
  1091. /** if true and run did not pass, then run doRunCompleted(..) */
  1092. final boolean logOnNotPass;
  1093. /** may be null */
  1094. protected final StreamsHandler streamsHandler;
  1095. /** true if the last run evaluation was ok */
  1096. boolean lastRunOk;
  1097. /** last run evaluated */
  1098. IRunStatus lastRun; // XXX small memory leak - cache hashcode instead?
  1099. /** @param label endsWith PASS || FAIL || ALL */
  1100. protected TestCompleteListener(
  1101. String label,
  1102. IRunValidator runValidator,
  1103. StreamsHandler streamsHandler) {
  1104. if (null == runValidator) {
  1105. runValidator = RunValidator.NORMAL;
  1106. }
  1107. this.label = (null == label? "" : label);
  1108. this.logOnPass = label.endsWith(PASS) || label.endsWith(ALL);
  1109. this.logOnNotPass = label.endsWith(FAIL) || label.endsWith(ALL);
  1110. this.runValidator = runValidator;
  1111. this.streamsHandler = streamsHandler;
  1112. }
  1113. public void runStarted(IRunStatus run) {
  1114. if (null != streamsHandler) {
  1115. streamsHandler.startListening();
  1116. }
  1117. }
  1118. /** subclasses implement this to do some per-test initialization */
  1119. protected void doRunStarted(IRunStatus run) {
  1120. }
  1121. /** subclasses implement this to do some per-suite initialization */
  1122. protected void doStartSuite(File suite) {
  1123. }
  1124. /** subclasses implement this to do end-of-suite processing */
  1125. protected void doEndSuite(File suite, long duration) {
  1126. }
  1127. @Override
  1128. public final void runCompleted(IRunStatus run) {
  1129. boolean doit = lastRunOk(run);
  1130. StreamsHandler.Result result = null;
  1131. if (null != streamsHandler) {
  1132. streamsHandler.endListening(doit);
  1133. }
  1134. if (doit) {
  1135. doRunCompleted(run, result);
  1136. }
  1137. }
  1138. /**
  1139. * @return true if run is ok per constructor specifications
  1140. */
  1141. protected boolean lastRunOk(IRunStatus run) {
  1142. if (lastRun != run) {
  1143. boolean passed = runValidator.runPassed(run);
  1144. lastRunOk = (passed ? logOnPass : logOnNotPass);
  1145. }
  1146. return lastRunOk;
  1147. }
  1148. /** @return "{classname}({pass}{,fail})" indicating when this runs */
  1149. @Override
  1150. public String toString() { // XXX add label?
  1151. return LangUtil.unqualifiedClassName(this)
  1152. + "(" + (logOnPass ? (logOnNotPass ? "pass, fail)" : "pass)")
  1153. : (logOnNotPass ? "fail)" : ")"));
  1154. }
  1155. /**
  1156. * Subclasses implement this to do some completion action
  1157. * @param run the IRunStatus for this completed run
  1158. * @param result the StreamsHandler.Result (if any - may be null)
  1159. */
  1160. public abstract void doRunCompleted(IRunStatus run, StreamsHandler.Result result);
  1161. }
  1162. /**
  1163. * Write XML for any test passed and/or failed.
  1164. * Must register with Runner for RunSpecIterator.class,
  1165. * most sensibly AjcTest.class.
  1166. */
  1167. class XmlLogger extends TestCompleteListener {
  1168. /**
  1169. * @param printer the component that prints any status - not null
  1170. * @param runValidator if null, use RunValidator.NORMAL
  1171. */
  1172. public XmlLogger(
  1173. String label,
  1174. StreamsHandler streamsHandler,
  1175. IRunValidator runValidator) {
  1176. super(label, runValidator, streamsHandler);
  1177. }
  1178. @Override
  1179. public void doRunCompleted(IRunStatus run, StreamsHandler.Result result) {
  1180. PrintStream out = streamsHandler.getLogStream();
  1181. out.println("");
  1182. XMLWriter writer = new XMLWriter(new PrintWriter(out, true));
  1183. Object id = run.getIdentifier();
  1184. if (!(id instanceof Runner.IteratorWrapper)) {
  1185. out.println(this + " not IteratorWrapper: "
  1186. + id.getClass().getName() + ": " + id);
  1187. return;
  1188. }
  1189. IRunIterator iter = ((Runner.IteratorWrapper) id).iterator;
  1190. if (!(iter instanceof RunSpecIterator)) {
  1191. out.println(this + " not RunSpecIterator: " + iter.getClass().getName()
  1192. + ": " + iter);
  1193. return;
  1194. }
  1195. ((RunSpecIterator) iter).spec.writeXml(writer);
  1196. out.flush();
  1197. }
  1198. }
  1199. /**
  1200. * Write junit style XML output (for incorporation into html test results and
  1201. * cruise control reports
  1202. * format is...
  1203. * <?xml version="1.0" encoding="UTF-8" ?>
  1204. * <testsuite errors="x" failures="x" name="suite-name" tests="xx" time="ss.ssss">
  1205. * <properties/>
  1206. * <testcase name="passingTest" time="s.hh"></testcase>
  1207. * <testcase name="failingTest" time="s.hh">
  1208. * <failure message="failureMessage" type="ExceptionType">free text</failure>
  1209. * </testcase>
  1210. * </testsuite>
  1211. */
  1212. class JUnitXMLLogger extends TestCompleteListener {
  1213. // private File suite;
  1214. private StringBuffer junitOutput;
  1215. private long startTimeMillis;
  1216. private int numTests = 0;
  1217. private int numFails = 0;
  1218. private DecimalFormat timeFormatter = new DecimalFormat("#.##");
  1219. public JUnitXMLLogger(
  1220. String label,
  1221. StreamsHandler streamsHandler,
  1222. IRunValidator runValidator) {
  1223. super(label + ALL, runValidator, streamsHandler);
  1224. junitOutput = new StringBuffer();
  1225. }
  1226. /* (non-Javadoc)
  1227. * @see org.aspectj.testing.drivers.TestCompleteListener#doRunCompleted(org.aspectj.testing.run.IRunStatus, org.aspectj.testing.util.StreamsHandler.Result)
  1228. */
  1229. @Override
  1230. public void doRunCompleted(IRunStatus run, Result result) {
  1231. long duration = System.currentTimeMillis() - startTimeMillis;
  1232. numTests++;
  1233. junitOutput.append("<testcase name=\"" + run.getIdentifier() + "\" ");
  1234. junitOutput.append("time=\"" + timeFormatter.format((duration)/1000.0f) + "\"");
  1235. junitOutput.append(">");
  1236. if (!run.runResult()) {
  1237. numFails++;
  1238. junitOutput.append("\n");
  1239. junitOutput.append("<failure message=\"test failed\" type=\"unknown\">\n");
  1240. // junitOutput.println(result.junitOutput);
  1241. // junitOutput.println(result.err);
  1242. junitOutput.append("</failure>\n");
  1243. }
  1244. junitOutput.append("</testcase>\n");
  1245. }
  1246. /* (non-Javadoc)
  1247. * @see org.aspectj.testing.drivers.TestCompleteListener#runStarted(org.aspectj.testing.run.IRunStatus)
  1248. */
  1249. @Override
  1250. public void runStarting(IRunStatus run) {
  1251. super.runStarting(run);
  1252. startTimeMillis = System.currentTimeMillis();
  1253. }
  1254. /* (non-Javadoc)
  1255. * @see org.aspectj.testing.drivers.TestCompleteListener#doEndSuite(java.io.File, long)
  1256. */
  1257. @Override
  1258. protected void doEndSuite(File suite, long duration) {
  1259. super.doEndSuite(suite, duration);
  1260. String suiteName = suite.getName();
  1261. // junit reporter doesn't like ".xml" on the end
  1262. suiteName = suiteName.substring(0,suiteName.indexOf('.'));
  1263. PrintStream out = streamsHandler.getLogStream();
  1264. out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
  1265. String timeStr = new DecimalFormat("#.##").format(duration/1000.0);
  1266. out.print("<testsuite errors=\"" + numFails + "\" failures=\"0\" ");
  1267. out.print("name=\"" + suite.getName() + "\" " );
  1268. out.println("tests=\"" + numTests + "\" time=\"" + timeStr + "\">");
  1269. out.print(junitOutput.toString());
  1270. out.println("</testsuite>");
  1271. }
  1272. /* (non-Javadoc)
  1273. * @see org.aspectj.testing.drivers.TestCompleteListener#doStartSuite(java.io.File)
  1274. */
  1275. @Override
  1276. protected void doStartSuite(File suite) {
  1277. super.doStartSuite(suite);
  1278. // this.suite = suite;
  1279. numTests = 0;
  1280. numFails = 0;
  1281. junitOutput = new StringBuffer();
  1282. }
  1283. }
  1284. /** log pass and/or failed runs */
  1285. class RunLogger extends TestCompleteListener {
  1286. final boolean logStreams;
  1287. final RunUtils.IRunStatusPrinter printer;
  1288. /**
  1289. * @param printer the component that prints any status - not null
  1290. * @param runValidator if null, use RunValidator.NORMAL
  1291. */
  1292. public RunLogger(
  1293. String label,
  1294. boolean logStreams,
  1295. StreamsHandler streamsHandler,
  1296. IRunValidator runValidator,
  1297. RunUtils.IRunStatusPrinter printer) {
  1298. super(label, runValidator, streamsHandler);
  1299. LangUtil.throwIaxIfNull(streamsHandler, "streamsHandler");
  1300. LangUtil.throwIaxIfNull(printer, "printer");
  1301. this.logStreams = logStreams;
  1302. this.printer = printer;
  1303. }
  1304. @Override
  1305. public void doRunCompleted(IRunStatus run, StreamsHandler.Result result) {
  1306. PrintStream out = streamsHandler.getLogStream();
  1307. printer.printRunStatus(out, run);
  1308. if (logStreams) {
  1309. if (!LangUtil.isEmpty(result.err)) {
  1310. out.println("--- error");
  1311. out.println(result.err);
  1312. }
  1313. if (!LangUtil.isEmpty(result.out)) {
  1314. out.println("--- ouput");
  1315. out.println(result.out);
  1316. }
  1317. }
  1318. out.println("");
  1319. }
  1320. }
  1321. /** trace time and memory between runStaring and runCompleted */
  1322. class TestTraceLogger extends TestCompleteListener {
  1323. private static final Runtime runtime = Runtime.getRuntime();
  1324. private long startTime;
  1325. private long startMemoryFree;
  1326. private final boolean verbose;
  1327. public TestTraceLogger(StreamsHandler handler) {
  1328. this(handler, true);
  1329. }
  1330. public TestTraceLogger(StreamsHandler handler, boolean verbose) {
  1331. super("-traceTestsAll", null, handler);
  1332. this.verbose = verbose;
  1333. }
  1334. @Override
  1335. public void runStarting(IRunStatus run) {
  1336. super.runStarting(run);
  1337. startTime = System.currentTimeMillis();
  1338. startMemoryFree = runtime.freeMemory();
  1339. }
  1340. @Override
  1341. public void doRunCompleted(IRunStatus run, StreamsHandler.Result result) {
  1342. long elapsed = System.currentTimeMillis() - startTime;
  1343. long free = runtime.freeMemory();
  1344. long used = startMemoryFree - free;
  1345. String label = run.runResult() ? "PASS " : "FAIL ";
  1346. PrintStream out = streamsHandler.getLogStream();
  1347. if (verbose) {
  1348. label = label
  1349. + "elapsed: " + LangUtil.toSizedString(elapsed, 7)
  1350. + " free: " + LangUtil.toSizedString(free, 10)
  1351. + " used: " + LangUtil.toSizedString(used, 10)
  1352. + " id: ";
  1353. }
  1354. out.println(label + renderId(run));
  1355. }
  1356. /** @return true - always trace tests */
  1357. protected boolean isFailLabel(String label) {
  1358. return true;
  1359. }
  1360. /** @return true - always trace tests */
  1361. protected boolean isPassLabel(String label) {
  1362. return true;
  1363. }
  1364. /**
  1365. * This implementation returns run identifier toString().
  1366. * Subclasses override this to render id as message suffix.
  1367. */
  1368. protected String renderId(IRunStatus run) {
  1369. return "" + run.getIdentifier();
  1370. }
  1371. }
  1372. // printing files
  1373. // AjcTest.Spec testSpec = AjcTest.unwrapSpec(run);
  1374. // if (null != testSpec) {
  1375. // CompilerRun.Spec compileSpec = AjcTest.unwrapCompilerRunSpec(testSpec);
  1376. // File dir = new File(testSpec.getSuiteDir(), testSpec.getTestDirOffset());
  1377. // List files = compileSpec.getPathsAsFile(dir);
  1378. // StringBuffer sb = new StringBuffer();
  1379. // for (Iterator iter = files.iterator(); iter.hasNext();) {
  1380. // File file = (File) iter.next();
  1381. // sb.append(" " + file.getPath().replace('\\','/').substring(2));
  1382. // }
  1383. // out.println("files: " + sb);
  1384. // }