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.

TestUtil.java 36KB

21 years ago
21 years ago
21 years ago
21 years ago
19 years ago
21 years ago
19 years ago
19 years ago
21 years ago
19 years ago
21 years ago
19 years ago
21 years ago
21 years ago
21 years ago
21 years ago
19 years ago
19 years ago
19 years ago
21 years ago
19 years ago
19 years ago
19 years ago
19 years ago
19 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
21 years ago
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045
  1. /* *******************************************************************
  2. * Copyright (c) 2002 Palo Alto Research Center, Incorporated (PARC).
  3. * All rights reserved.
  4. * This program and the accompanying materials are made available
  5. * under the terms of the Eclipse Public License v1.0
  6. * which accompanies this distribution and is available at
  7. * http://www.eclipse.org/legal/epl-v10.html
  8. *
  9. * Contributors:
  10. * Xerox/PARC initial implementation
  11. * ******************************************************************/
  12. package org.aspectj.testing.util;
  13. import java.io.BufferedReader;
  14. import java.io.ByteArrayOutputStream;
  15. import java.io.DataInputStream;
  16. import java.io.File;
  17. import java.io.FileInputStream;
  18. import java.io.IOException;
  19. import java.io.InputStream;
  20. import java.io.PrintStream;
  21. import java.io.PrintWriter;
  22. import java.io.StringReader;
  23. import java.io.StringWriter;
  24. import java.lang.reflect.InvocationTargetException;
  25. import java.lang.reflect.Method;
  26. import java.lang.reflect.Modifier;
  27. import java.net.MalformedURLException;
  28. import java.net.URL;
  29. import java.util.ArrayList;
  30. import java.util.Arrays;
  31. import java.util.Collection;
  32. import java.util.Collections;
  33. import java.util.Enumeration;
  34. import java.util.HashMap;
  35. import java.util.HashSet;
  36. import java.util.Iterator;
  37. import java.util.List;
  38. import java.util.Map;
  39. import java.util.Properties;
  40. import java.util.Set;
  41. import jdiff.text.FileLine;
  42. import jdiff.util.Diff;
  43. import jdiff.util.DiffNormalOutput;
  44. import junit.framework.Assert;
  45. import junit.framework.Test;
  46. import junit.framework.TestCase;
  47. import junit.framework.TestResult;
  48. import junit.framework.TestSuite;
  49. import junit.runner.TestCaseClassLoader;
  50. import org.aspectj.bridge.IMessageHandler;
  51. import org.aspectj.bridge.MessageUtil;
  52. import org.aspectj.util.FileUtil;
  53. import org.aspectj.util.LangUtil;
  54. import org.aspectj.util.Reflection;
  55. /**
  56. * Things that junit should perhaps have, but doesn't. Note the file-comparison
  57. * methods require JDiff to run, but JDiff types are not required to resolve
  58. * this class. Also, the bytecode weaver is required to compare class files, but
  59. * not to compare other files.
  60. */
  61. public final class TestUtil {
  62. private static final boolean JAVA_5_VM;
  63. private static final String ASPECTJRT_KEY = "aspectjrt";
  64. private static final String TESTING_CLIENT_KEY = "testing-client";
  65. public static final URL BAD_URL;
  66. private static final File LIB_DIR;
  67. private static final Properties LIB_RPATHS;
  68. private static final Map LIB_ENTRIES;
  69. private static File ASPECTJRT_PATH;
  70. static {
  71. {
  72. String[] paths = { "sp:aspectjrt.path", "sp:aspectjrt.jar",
  73. "../lib/test/aspectjrt.jar", "../aj-build/jars/aspectj5rt-all.jar",
  74. "../aj-build/jars/runtime.jar",
  75. "../runtime/bin"};
  76. ASPECTJRT_PATH = FileUtil.getBestFile(paths);
  77. }
  78. {
  79. boolean j5 = false;
  80. try {
  81. Class.forName("java.lang.annotation.Annotation");
  82. j5 = true;
  83. } catch (Throwable t) {
  84. }
  85. JAVA_5_VM = j5;
  86. }
  87. {
  88. URL url = null;
  89. try {
  90. url = new URL("http://eclipse.org/BADURL");
  91. } catch (MalformedURLException e) {
  92. // ignore - hopefully never
  93. }
  94. BAD_URL = url;
  95. }
  96. {
  97. File file = new File("lib");
  98. if (!isLibDir(file)) {
  99. File cur = new File(".").getAbsoluteFile();
  100. File parent = cur.getParentFile();
  101. while (null != parent) {
  102. file = new File(parent, "lib");
  103. if (isLibDir(file)) {
  104. break;
  105. }
  106. parent = parent.getParentFile();
  107. }
  108. if (null == parent) {
  109. file = new File("NOT IN ASPECTJ TREE");
  110. }
  111. }
  112. LIB_DIR = file;
  113. }
  114. LIB_RPATHS = new Properties();
  115. LIB_RPATHS.setProperty(ASPECTJRT_KEY, "tests/aspectjrt.jar");
  116. LIB_RPATHS.setProperty(TESTING_CLIENT_KEY, "tests/testing-client.jar");
  117. // TODO support others loaded dynamically
  118. Map map = new HashMap();
  119. for (Iterator iter = LIB_RPATHS.keySet().iterator(); iter.hasNext();) {
  120. String key = (String) iter.next();
  121. String path = LIB_RPATHS.getProperty(key);
  122. File file = null;
  123. URL url = null;
  124. try {
  125. file = libFile(path);
  126. url = libURL(path);
  127. } catch (IllegalArgumentException e) {
  128. file = new File(path + " not found");
  129. url = BAD_URL;
  130. } finally {
  131. map.put(key + ".file", file);
  132. map.put(key + ".url", url);
  133. }
  134. }
  135. // TODO support changing entries, etc.
  136. LIB_ENTRIES = Collections.unmodifiableMap(map);
  137. }
  138. private static boolean isLibDir(File lib) {
  139. return new File(lib, "test" + File.separator + "aspectjrt.jar").exists();
  140. }
  141. private TestUtil() {
  142. super();
  143. }
  144. public static boolean is15VMOrGreater() { return JAVA_5_VM;}
  145. public static File aspectjrtPath() {
  146. return ASPECTJRT_PATH;
  147. }
  148. public static URL fileToURL(File file) {
  149. try {
  150. return file.toURL();
  151. } catch (MalformedURLException e) {
  152. return null;
  153. }
  154. }
  155. public static String filesToPath(File[] entries) {
  156. return toPath(entries);
  157. }
  158. public static String urlsToPath(URL[] entries) {
  159. return toPath(entries);
  160. }
  161. /**
  162. * untyped interface for mixed entries
  163. */
  164. public static String filesOrurlsToPath(Object[] entries) {
  165. return toPath(entries);
  166. }
  167. /**
  168. * This relies on these being File (where toString() == getPath())
  169. * or URL (where toString() == toExternalForm()).
  170. * @param entries the Object[] of File or URL elements
  171. * @return the String with entries dlimited by the File.pathSeparator
  172. */
  173. private static String toPath(Object[] entries) {
  174. if ((null == entries) || (0 == entries.length)) {
  175. return "";
  176. }
  177. StringBuffer path = new StringBuffer();
  178. boolean started = false;
  179. for (int i = 0; i < entries.length; i++) {
  180. if (null != entries[i]) {
  181. if (started) {
  182. path.append(File.pathSeparator);
  183. } else {
  184. started = true;
  185. }
  186. path.append(entries[i].toString());
  187. }
  188. }
  189. return path.toString();
  190. }
  191. /**
  192. * @param input the String to parse for [on|off|true|false]
  193. * @throws IllegalArgumentException if input is bad
  194. **/
  195. public static boolean parseBoolean(String input) {
  196. return parseBoolean(input, true);
  197. }
  198. /**
  199. * @param input the String to parse for [on|off|true|false]
  200. * @param iaxOnError if true and input is bad, throw
  201. * IllegalArgumentException
  202. * @return true if input is true, false otherwise
  203. * @throws IllegalArgumentException if iaxOnError and input is bad
  204. */
  205. public static boolean parseBoolean(final String input, boolean iaxOnError) {
  206. final String syntax = ": [on|off|true|false]";
  207. if (null == input) {
  208. return false;
  209. }
  210. String lc = input.trim().toLowerCase();
  211. boolean result = false;
  212. boolean valid = false;
  213. switch (lc.length()) {
  214. case 2:
  215. if (valid = "on".equals(lc)) {
  216. result = true;
  217. }
  218. break;
  219. case 3:
  220. valid = "off".equals(lc);
  221. break;
  222. case 4:
  223. if (valid = "true".equals(lc)) {
  224. result = true;
  225. }
  226. break;
  227. case 5:
  228. valid = "false".equals(lc);
  229. break;
  230. }
  231. if (iaxOnError && !valid) {
  232. throw new IllegalArgumentException(input + syntax);
  233. }
  234. return result;
  235. }
  236. public static File aspectjrtJarFile() {
  237. return (File) LIB_ENTRIES.get(ASPECTJRT_KEY + ".file");
  238. }
  239. public static URL aspectjrtJarURL() {
  240. return (URL) LIB_ENTRIES.get(ASPECTJRT_KEY + ".url");
  241. }
  242. public static File testingClientJarFile() {
  243. return (File) LIB_ENTRIES.get(TESTING_CLIENT_KEY + ".file");
  244. }
  245. public static URL testingClientJarURL() {
  246. return (URL) LIB_ENTRIES.get(TESTING_CLIENT_KEY + ".url");
  247. }
  248. /**
  249. *
  250. * @param rpath the String relative path from the library directory
  251. * to a resource that must exist (may be a directory),
  252. * using forward slashes as a file separator
  253. * @return the File path
  254. * @throws IllegalArgumentException if no such directory or file
  255. */
  256. public static File libFile(String rpath) {
  257. if ((null == rpath) || (0 == rpath.length())) {
  258. throw new IllegalArgumentException("no input");
  259. }
  260. rpath = rpath.replace('/', File.separatorChar);
  261. File result = new File(LIB_DIR, rpath);
  262. if (result.exists()) {
  263. return result;
  264. }
  265. throw new IllegalArgumentException("not in " + LIB_DIR + ": " + rpath);
  266. }
  267. /**
  268. * Like libPath, only it returns a URL.
  269. * @return URL or null if it does not exist
  270. * @throws IllegalArgumentException if no such directory or file
  271. */
  272. public static URL libURL(String rpath) {
  273. File file = libFile(rpath);
  274. try {
  275. return file.toURL();
  276. } catch (MalformedURLException e) {
  277. throw new IllegalArgumentException("bad URL from: " + file);
  278. }
  279. }
  280. // ---- arrays
  281. public static void assertArrayEquals(String msg, Object[] expected,
  282. Object[] found) {
  283. TestCase.assertEquals(msg, Arrays.asList(expected), Arrays
  284. .asList(found));
  285. }
  286. // ---- unordered
  287. public static void assertSetEquals(Collection expected, Collection found) {
  288. assertSetEquals(null, expected, found);
  289. }
  290. public static void assertSetEquals(String msg, Object[] expected,
  291. Object[] found) {
  292. assertSetEquals(msg, Arrays.asList(expected), Arrays.asList(found));
  293. }
  294. public static void assertSetEquals(String msg, Collection expected,
  295. Collection found) {
  296. msg = (msg == null) ? "" : msg + ": ";
  297. Set results1 = new HashSet(found);
  298. results1.removeAll(expected);
  299. Set results2 = new HashSet(expected);
  300. results2.removeAll(found);
  301. if (results1.isEmpty()) {
  302. TestCase.assertTrue(msg + "Expected but didn't find: "
  303. + results2.toString(), results2.isEmpty());
  304. } else if (results2.isEmpty()) {
  305. TestCase.assertTrue(msg + "Didn't expect: " + results1.toString(),
  306. results1.isEmpty());
  307. } else {
  308. TestCase.assertTrue(msg + "Expected but didn't find: "
  309. + results2.toString() + "\nDidn't expect: "
  310. + results1.toString(), false);
  311. }
  312. }
  313. // ---- objects
  314. public static void assertCommutativeEquals(Object a, Object b,
  315. boolean should) {
  316. TestCase.assertEquals(a + " equals " + b, should, a.equals(b));
  317. TestCase.assertEquals(b + " equals " + a, should, b.equals(a));
  318. assertHashEquals(a, b, should);
  319. }
  320. private static void assertHashEquals(Object s, Object t, boolean should) {
  321. if (should) {
  322. TestCase.assertTrue(s + " does not hash to same as " + t, s
  323. .hashCode() == t.hashCode());
  324. } else {
  325. if (s.hashCode() == t.hashCode()) {
  326. System.err.println("warning: hash collision with hash = "
  327. + t.hashCode());
  328. System.err.println(" for " + s);
  329. System.err.println(" and " + t);
  330. }
  331. }
  332. }
  333. // -- reflective stuff
  334. public static void runMain(String classPath, String className) {
  335. runMethod(classPath, className, "main", new Object[] { new String[0] });
  336. }
  337. public static Object runMethod(String classPath, String className,
  338. String methodName, Object[] args) {
  339. classPath += File.pathSeparator + System.getProperty("java.class.path");
  340. ClassLoader loader = new TestCaseClassLoader(classPath);
  341. Class c = null;
  342. try {
  343. c = loader.loadClass(className);
  344. } catch (ClassNotFoundException e) {
  345. Assert.assertTrue("unexpected exception: " + e, false);
  346. }
  347. return Reflection.invokestaticN(c, methodName, args);
  348. }
  349. /**
  350. * Checks that two multi-line strings have the same value. Each line is
  351. * trimmed before comparision Produces an error on the particular line of
  352. * conflict
  353. */
  354. public static void assertMultiLineStringEquals(String message, String s1,
  355. String s2) {
  356. try {
  357. BufferedReader r1 = new BufferedReader(new StringReader(s1));
  358. BufferedReader r2 = new BufferedReader(new StringReader(s2));
  359. List lines = new ArrayList();
  360. String l1, l2;
  361. int index = 1;
  362. while (true) {
  363. l1 = readNonBlankLine(r1);
  364. l2 = readNonBlankLine(r2);
  365. if (l1 == null || l2 == null)
  366. break;
  367. if (l1.equals(l2)) {
  368. lines.add(l1);
  369. } else {
  370. showContext(lines);
  371. Assert.assertEquals(message + "(line " + index + "):\n"+l1+"\n"+l2, l1,
  372. l2);
  373. }
  374. index++;
  375. }
  376. if (l1 != null)
  377. showContext(lines);
  378. Assert.assertTrue(message + ": unexpected " + l1, l1 == null);
  379. if (l2 != null)
  380. showContext(lines);
  381. Assert.assertTrue(message + ": unexpected " + l2, l2 == null);
  382. } catch (IOException ioe) {
  383. Assert.assertTrue(message + ": caught " + ioe.getMessage(), false);
  384. }
  385. }
  386. private static void showContext(List lines) {
  387. int n = lines.size();
  388. for (int i = Math.max(0, n - 8); i < n; i++) {
  389. System.err.println(lines.get(i));
  390. }
  391. }
  392. private static String readNonBlankLine(BufferedReader r) throws IOException {
  393. String l = r.readLine();
  394. if (l == null)
  395. return null;
  396. l = l.trim();
  397. // comment to include comments when reading
  398. int commentLoc = l.indexOf("//");
  399. if (-1 != commentLoc) {
  400. l = l.substring(0, commentLoc).trim();
  401. }
  402. if ("".equals(l))
  403. return readNonBlankLine(r);
  404. return l;
  405. }
  406. /**
  407. * If there is an expected dir, expect each file in its subtree to match a
  408. * corresponding actual file in the base directory. This does NOT check that
  409. * all actual files have corresponding expected files. This ignores
  410. * directory paths containing "CVS".
  411. *
  412. * @param handler
  413. * the IMessageHandler sink for error messages
  414. * @param expectedBaseDir
  415. * the File path to the directory containing expected files, all
  416. * of which are compared with any corresponding actual files
  417. * @param actualBaseDir
  418. * the File path to the base directory from which to find any
  419. * actual files corresponding to expected files.
  420. * @return true if all files in the expectedBaseDir directory tree have
  421. * matching files in the actualBaseDir directory tree.
  422. */
  423. public static boolean sameDirectoryContents(final IMessageHandler handler,
  424. final File expectedBaseDir, final File actualBaseDir,
  425. final boolean fastFail) {
  426. LangUtil.throwIaxIfNull(handler, "handler");
  427. if (!FileUtil.canReadDir(expectedBaseDir)) {
  428. MessageUtil.fail(handler, " expected dir not found: "
  429. + expectedBaseDir);
  430. return false;
  431. }
  432. if (!FileUtil.canReadDir(actualBaseDir)) {
  433. MessageUtil
  434. .fail(handler, " actual dir not found: " + actualBaseDir);
  435. return false;
  436. }
  437. String[] paths = FileUtil.listFiles(expectedBaseDir);
  438. boolean result = true;
  439. for (int i = 0; i < paths.length; i++) {
  440. if (-1 != paths[i].indexOf("CVS")) {
  441. continue;
  442. }
  443. if (!sameFiles(handler, expectedBaseDir, actualBaseDir, paths[i])
  444. && result) {
  445. result = false;
  446. if (fastFail) {
  447. break;
  448. }
  449. }
  450. }
  451. return result;
  452. }
  453. // ------------ File-comparison utilities (XXX need their own class...)
  454. /**
  455. * Test interface to compare two files, line by line, and report differences
  456. * as one FAIL message if a handler is supplied. This preprocesses .class
  457. * files by disassembling.
  458. *
  459. * @param handler
  460. * the IMessageHandler for any FAIL messages (null to ignore)
  461. * @param expectedFile
  462. * the File path to the canonical file
  463. * @param actualFile
  464. * the File path to the actual file, if any
  465. * @return true if the input files are the same, based on per-line
  466. * comparisons
  467. */
  468. public static boolean sameFiles(IMessageHandler handler, File expectedFile,
  469. File actualFile) {
  470. return doSameFile(handler, null, null, expectedFile, actualFile);
  471. }
  472. /**
  473. * Test interface to compare two files, line by line, and report differences
  474. * as one FAIL message if a handler is supplied. This preprocesses .class
  475. * files by disassembling. This method assumes that the files are at the
  476. * same offset from two respective base directories.
  477. *
  478. * @param handler
  479. * the IMessageHandler for any FAIL messages (null to ignore)
  480. * @param expectedBaseDir
  481. * the File path to the canonical file base directory
  482. * @param actualBaseDir
  483. * the File path to the actual file base directory
  484. * @param path
  485. * the String path offset from the base directories
  486. * @return true if the input files are the same, based on per-line
  487. * comparisons
  488. */
  489. public static boolean sameFiles(IMessageHandler handler,
  490. File expectedBaseDir, File actualBaseDir, String path) {
  491. File actualFile = new File(actualBaseDir, path);
  492. File expectedFile = new File(expectedBaseDir, path);
  493. return doSameFile(handler, expectedBaseDir, actualBaseDir,
  494. expectedFile, actualFile);
  495. }
  496. /**
  497. * This does the work, selecting a lineator subclass and converting public
  498. * API's to JDiff APIs for comparison. Currently, all jdiff interfaces are
  499. * method-local, so this class will load without it; if we do use it, we can
  500. * avoid the duplication.
  501. */
  502. private static boolean doSameFile(IMessageHandler handler,
  503. File expectedBaseDir, File actualBaseDir, File expectedFile,
  504. File actualFile) {
  505. String path = expectedFile.getPath();
  506. // XXX permit user to specify lineator
  507. ILineator lineator = Lineator.TEXT;
  508. if (path.endsWith(".class")) {
  509. if (ClassLineator.haveDisassembler()) {
  510. lineator = Lineator.CLASS;
  511. } else {
  512. MessageUtil.abort(handler,
  513. "skipping - dissassembler not available");
  514. return false;
  515. }
  516. }
  517. CanonicalLine[] actualLines = null;
  518. CanonicalLine[] expectedLines = null;
  519. try {
  520. actualLines = lineator.getLines(handler, actualFile, actualBaseDir);
  521. expectedLines = lineator.getLines(handler, expectedFile,
  522. expectedBaseDir);
  523. } catch (IOException e) {
  524. MessageUtil.fail(handler, "rendering lines ", e);
  525. return false;
  526. }
  527. if (!LangUtil.isEmpty(actualLines) && !LangUtil.isEmpty(expectedLines)) {
  528. // here's the transmutation back to jdiff - extract if publishing
  529. // JDiff
  530. CanonicalLine[][] clines = new CanonicalLine[][] { expectedLines,
  531. actualLines };
  532. FileLine[][] flines = new FileLine[2][];
  533. for (int i = 0; i < clines.length; i++) {
  534. CanonicalLine[] cline = clines[i];
  535. FileLine[] fline = new FileLine[cline.length];
  536. for (int j = 0; j < fline.length; j++) {
  537. fline[j] = new FileLine(cline[j].canonical, cline[j].line);
  538. }
  539. flines[i] = fline;
  540. }
  541. Diff.change edits = new Diff(flines[0], flines[1]).diff_2(false);
  542. if ((null == edits) || (0 == (edits.inserted + edits.deleted))) {
  543. // XXX confirm with jdiff that null means no edits
  544. return true;
  545. } else {
  546. // String m = render(handler, edits, flines[0], flines[1]);
  547. StringWriter writer = new StringWriter();
  548. DiffNormalOutput out = new DiffNormalOutput(flines[0],
  549. flines[1]);
  550. out.setOut(writer);
  551. out.setLineSeparator(LangUtil.EOL);
  552. try {
  553. out.writeScript(edits);
  554. } catch (IOException e) {
  555. MessageUtil.fail(handler, "rendering edits", e);
  556. } finally {
  557. if (null != writer) {
  558. try {
  559. writer.close();
  560. } catch (IOException e) {
  561. MessageUtil.fail(handler,
  562. "closing after rendering edits", e);
  563. }
  564. }
  565. }
  566. String message = "diff between " + path + " in expected dir "
  567. + expectedBaseDir + " and actual dir " + actualBaseDir
  568. + LangUtil.EOL + writer.toString();
  569. MessageUtil.fail(handler, message);
  570. }
  571. }
  572. return false;
  573. }
  574. public static String cleanTestName(String name) {
  575. name = name.replace(' ', '_');
  576. return name;
  577. }
  578. public static Test skipTest(String tests) {
  579. // could printStackTrace to give more context if needed...
  580. System.err.println("skipping tests " + tests);
  581. return testNamed("skipping tests " + tests);
  582. }
  583. public static Test testNamed(String named) {
  584. final String name = cleanTestName(named);
  585. return new Test() {
  586. public int countTestCases() {
  587. return 1;
  588. }
  589. public void run(TestResult r) {
  590. r.startTest(this);
  591. r.endTest(this);
  592. }
  593. public String toString() { return name; }
  594. };
  595. }
  596. /**
  597. * @param sink the TestSuite sink to add result to
  598. * @param sourceName the String fully-qualified name of the class with a suite() method to load
  599. */
  600. public static void loadTestsReflectively(TestSuite sink, String sourceName, boolean ignoreError) {
  601. Throwable thrown = null;
  602. try {
  603. ClassLoader loader = sink.getClass().getClassLoader();
  604. Class sourceClass = loader.loadClass(sourceName);
  605. if (!Modifier.isPublic(sourceClass.getModifiers())) {
  606. errorSuite(sink, sourceName, "not public class");
  607. return;
  608. }
  609. Method suiteMethod = sourceClass.getMethod("suite", new Class[0]);
  610. int mods = suiteMethod.getModifiers();
  611. if (!Modifier.isStatic(mods) || !Modifier.isPublic(mods)) {
  612. errorSuite(sink, sourceName, "not static method");
  613. return;
  614. }
  615. if (!Modifier.isPublic(mods)) {
  616. errorSuite(sink, sourceName, "not public method");
  617. return;
  618. }
  619. if (!Test.class.isAssignableFrom(suiteMethod.getReturnType())) {
  620. errorSuite(sink, sourceName, "suite() does not return Test");
  621. return;
  622. }
  623. Object result = suiteMethod.invoke(null, new Object[0]);
  624. Test test = (Test) result;
  625. if (!(test instanceof TestSuite)) {
  626. sink.addTest(test);
  627. } else {
  628. TestSuite source = (TestSuite) test;
  629. Enumeration tests = source.tests();
  630. while (tests.hasMoreElements()) {
  631. sink.addTest((Test) tests.nextElement());
  632. }
  633. }
  634. } catch (ClassNotFoundException e) {
  635. thrown = e;
  636. } catch (SecurityException e) {
  637. thrown = e;
  638. } catch (NoSuchMethodException e) {
  639. thrown = e;
  640. } catch (IllegalArgumentException e) {
  641. thrown = e;
  642. } catch (IllegalAccessException e) {
  643. thrown = e;
  644. } catch (InvocationTargetException e) {
  645. thrown = e;
  646. }
  647. if (null != thrown) {
  648. if (ignoreError) {
  649. System.err.println("Error loading " + sourceName);
  650. thrown.printStackTrace(System.err);
  651. } else {
  652. errorSuite(sink, sourceName, thrown);
  653. }
  654. }
  655. }
  656. private static void errorSuite(TestSuite sink, String sourceName,
  657. Throwable thrown) {
  658. sink.addTest(new ErrorTest(sourceName, thrown));
  659. }
  660. private static void errorSuite(TestSuite sink, String sourceName, String err) {
  661. String message = "bad " + sourceName + ": " + err;
  662. sink.addTest(new ErrorTest(message));
  663. }
  664. /**
  665. * Junit test failure, e.g., to report suite initialization errors at test time.
  666. */
  667. public static class ErrorTest implements Test {
  668. private final Throwable thrown;
  669. public ErrorTest(Throwable thrown) {
  670. this.thrown = thrown;
  671. }
  672. public ErrorTest(String message) {
  673. this.thrown = new Error(message);
  674. }
  675. public ErrorTest(String message, Throwable thrown) {
  676. this(new TestError(message, thrown));
  677. }
  678. public int countTestCases() {
  679. return 1;
  680. }
  681. public void run(TestResult result) {
  682. result.startTest(this);
  683. result.addError(this, thrown);
  684. }
  685. }
  686. /**
  687. * Nested exception - remove when using 1.4 or later.
  688. */
  689. public static class TestError extends Error {
  690. private Throwable thrown;
  691. public TestError(String message) {
  692. super(message);
  693. }
  694. public TestError(String message, Throwable thrown) {
  695. super(message);
  696. this.thrown = thrown;
  697. }
  698. public Throwable getCause() {
  699. return thrown;
  700. }
  701. public void printStackTrace() {
  702. printStackTrace(System.err);
  703. }
  704. public void printStackTrace(PrintStream ps) {
  705. printStackTrace(new PrintWriter(ps));
  706. }
  707. public void printStackTrace(PrintWriter pw) {
  708. super.printStackTrace(pw);
  709. if (null != thrown) {
  710. pw.print("Caused by: ");
  711. thrown.printStackTrace(pw);
  712. }
  713. }
  714. }
  715. /** component that reduces file to CanonicalLine[] */
  716. public static interface ILineator {
  717. /** Lineator suitable for text files */
  718. public static final ILineator TEXT = new TextLineator();
  719. /** Lineator suitable for class files (disassembles first) */
  720. public static final ILineator CLASS = new ClassLineator();
  721. /**
  722. * Reduce file to CanonicalLine[].
  723. *
  724. * @param handler
  725. * the IMessageHandler for errors (may be null)
  726. * @param file
  727. * the File to render
  728. * @param basedir
  729. * the File for the base directory (may be null)
  730. * @return CanonicalLine[] of lines - not null, but perhaps empty
  731. */
  732. CanonicalLine[] getLines(IMessageHandler handler, File file,
  733. File basedir) throws IOException;
  734. }
  735. /** alias for jdiff FileLine to avoid client binding */
  736. public static class CanonicalLine {
  737. public static final CanonicalLine[] NO_LINES = new CanonicalLine[0];
  738. /** canonical variant of line for comparison */
  739. public final String canonical;
  740. /** actual line, for logging */
  741. public final String line;
  742. public CanonicalLine(String canonical, String line) {
  743. this.canonical = canonical;
  744. this.line = line;
  745. }
  746. public String toString() {
  747. return line;
  748. }
  749. }
  750. private abstract static class Lineator implements ILineator {
  751. /**
  752. * Reduce file to CanonicalLine[].
  753. *
  754. * @param handler
  755. * the IMessageHandler for errors (may be null)
  756. * @param file
  757. * the File to render
  758. * @param basedir
  759. * the File for the base directory (may be null)
  760. */
  761. public CanonicalLine[] getLines(IMessageHandler handler, File file,
  762. File basedir) throws IOException {
  763. if (!file.canRead() || !file.isFile()) {
  764. MessageUtil.error(handler, "not readable file: " + basedir
  765. + " - " + file);
  766. return null;
  767. }
  768. // capture file as FileLine[]
  769. InputStream in = null;
  770. /* String path = */FileUtil.normalizedPath(file, basedir);
  771. LineStream capture = new LineStream();
  772. try {
  773. lineate(capture, handler, basedir, file);
  774. } catch (IOException e) {
  775. MessageUtil
  776. .fail(handler,
  777. "NormalizedCompareFiles IOException reading "
  778. + file, e);
  779. return null;
  780. } finally {
  781. if (null != in) {
  782. try {
  783. in.close();
  784. } catch (IOException e) {
  785. } // ignore
  786. }
  787. capture.flush();
  788. capture.close();
  789. }
  790. String missed = capture.getMissed();
  791. if (!LangUtil.isEmpty(missed)) {
  792. MessageUtil.warn(handler,
  793. "NormalizedCompareFiles missed input: " + missed);
  794. return null;
  795. } else {
  796. String[] lines = capture.getLines();
  797. CanonicalLine[] result = new CanonicalLine[lines.length];
  798. for (int i = 0; i < lines.length; i++) {
  799. result[i] = new CanonicalLine(lines[i], lines[i]);
  800. }
  801. return result;
  802. }
  803. }
  804. protected abstract void lineate(PrintStream sink,
  805. IMessageHandler handler, File basedir, File file)
  806. throws IOException;
  807. }
  808. private static class TextLineator extends Lineator {
  809. protected void lineate(PrintStream sink, IMessageHandler handler,
  810. File basedir, File file) throws IOException {
  811. InputStream in = null;
  812. try {
  813. in = new FileInputStream(file);
  814. FileUtil.copyStream(new DataInputStream(in), sink);
  815. } finally {
  816. try {
  817. in.close();
  818. } catch (IOException e) {
  819. } // ignore
  820. }
  821. }
  822. }
  823. public static class ClassLineator extends Lineator {
  824. protected void lineate(PrintStream sink, IMessageHandler handler,
  825. File basedir, File file) throws IOException {
  826. String name = FileUtil.fileToClassName(basedir, file);
  827. // XXX re-enable preflight?
  828. // if ((null != basedir) && (path.length()-6 != name.length())) {
  829. // MessageUtil.error(handler, "unexpected class name \""
  830. // + name + "\" for path " + path);
  831. // return null;
  832. // }
  833. disassemble(handler, basedir, name, sink);
  834. }
  835. public static boolean haveDisassembler() {
  836. try {
  837. return (null != Class
  838. .forName("org.aspectj.weaver.bcel.LazyClassGen"));
  839. } catch (ClassNotFoundException e) {
  840. // XXX fix
  841. // System.err.println(e.getMessage());
  842. // e.printStackTrace(System.err);
  843. return false;
  844. }
  845. }
  846. /** XXX dependency on bcweaver/bcel */
  847. private static void disassemble(IMessageHandler handler, File basedir,
  848. String name, PrintStream out) throws IOException {
  849. // LazyClassGen.disassemble(FileUtil.normalizedPath(basedir), name,
  850. // capture);
  851. Throwable thrown = null;
  852. String basedirPath = FileUtil.normalizedPath(basedir);
  853. // XXX use reflection utilities to invoke dissassembler?
  854. try {
  855. // XXX need test to detect when this is refactored
  856. Class c = Class.forName("org.aspectj.weaver.bcel.LazyClassGen");
  857. Method m = c.getMethod("disassemble", new Class[] {
  858. String.class, String.class, PrintStream.class });
  859. m.invoke(null, new Object[] { basedirPath, name, out });
  860. } catch (ClassNotFoundException e) {
  861. thrown = e;
  862. } catch (NoSuchMethodException e) {
  863. thrown = e;
  864. } catch (IllegalAccessException e) {
  865. thrown = e;
  866. } catch (InvocationTargetException e) {
  867. Throwable t = e.getTargetException();
  868. if (t instanceof IOException) {
  869. throw (IOException) t;
  870. }
  871. thrown = t;
  872. }
  873. if (null != thrown) {
  874. MessageUtil.fail(handler, "disassembling " + name + " path: "
  875. + basedirPath, thrown);
  876. }
  877. }
  878. }
  879. /**
  880. * Capture PrintStream output to String[] (delimiting component String on
  881. * println()), also showing any missed text.
  882. */
  883. public static class LineStream extends PrintStream {
  884. StringBuffer sb = new StringBuffer();
  885. ByteArrayOutputStream missed;
  886. ArrayList sink;
  887. public LineStream() {
  888. super(new ByteArrayOutputStream());
  889. this.sink = new ArrayList();
  890. missed = (ByteArrayOutputStream) out;
  891. }
  892. /** @return any text not captured by our overrides */
  893. public String getMissed() {
  894. return missed.toString();
  895. }
  896. /** clear captured lines (but not missed text) */
  897. public void clear() {
  898. sink.clear();
  899. }
  900. /**
  901. * Get String[] of lines printed, delimited by println(..) calls.
  902. *
  903. * @return lines printed, exclusive of any not yet terminated by newline
  904. */
  905. public String[] getLines() {
  906. return (String[]) sink.toArray(new String[0]);
  907. }
  908. // ---------- PrintStream overrides
  909. public void println(Object x) {
  910. println(x.toString());
  911. }
  912. public void print(Object obj) {
  913. print(obj.toString());
  914. }
  915. public void println(char c) {
  916. sb.append(c);
  917. println();
  918. }
  919. public void println(char[] c) {
  920. sb.append(c);
  921. println();
  922. }
  923. public void print(char c) {
  924. sb.append(c);
  925. }
  926. public void print(char[] c) {
  927. sb.append(c);
  928. }
  929. public void println(String s) {
  930. print(s);
  931. println();
  932. }
  933. public void print(String s) {
  934. sb.append(s);
  935. }
  936. public void println() {
  937. String line = sb.toString();
  938. sink.add(line);
  939. sb.setLength(0);
  940. }
  941. }
  942. }