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

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