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.

TestConverter.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. /*
  2. * $Id: TestConverter.java,v 1.23 2003/03/07 10:09:30 jeremias Exp $
  3. * ============================================================================
  4. * The Apache Software License, Version 1.1
  5. * ============================================================================
  6. *
  7. * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without modifica-
  10. * tion, are permitted provided that the following conditions are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright notice,
  13. * this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright notice,
  16. * this list of conditions and the following disclaimer in the documentation
  17. * and/or other materials provided with the distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if any, must
  20. * include the following acknowledgment: "This product includes software
  21. * developed by the Apache Software Foundation (http://www.apache.org/)."
  22. * Alternately, this acknowledgment may appear in the software itself, if
  23. * and wherever such third-party acknowledgments normally appear.
  24. *
  25. * 4. The names "FOP" and "Apache Software Foundation" must not be used to
  26. * endorse or promote products derived from this software without prior
  27. * written permission. For written permission, please contact
  28. * apache@apache.org.
  29. *
  30. * 5. Products derived from this software may not be called "Apache", nor may
  31. * "Apache" appear in their name, without prior written permission of the
  32. * Apache Software Foundation.
  33. *
  34. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
  35. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  36. * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  37. * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  38. * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
  39. * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  40. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  41. * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  42. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  43. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44. * ============================================================================
  45. *
  46. * This software consists of voluntary contributions made by many individuals
  47. * on behalf of the Apache Software Foundation and was originally created by
  48. * James Tauber <jtauber@jtauber.com>. For more information on the Apache
  49. * Software Foundation, please see <http://www.apache.org/>.
  50. */
  51. package org.apache.fop.tools;
  52. import org.apache.fop.apps.Driver;
  53. import org.apache.fop.apps.FOInputHandler;
  54. import org.apache.fop.apps.FOPException;
  55. import org.apache.fop.apps.InputHandler;
  56. import org.apache.fop.apps.XSLTInputHandler;
  57. import org.apache.fop.fo.FOUserAgent;
  58. import org.apache.avalon.framework.logger.ConsoleLogger;
  59. import org.apache.avalon.framework.logger.AbstractLogEnabled;
  60. import java.io.File;
  61. import java.io.InputStream;
  62. import java.util.Map;
  63. import javax.xml.parsers.DocumentBuilder;
  64. import javax.xml.parsers.DocumentBuilderFactory;
  65. import org.w3c.dom.Document;
  66. import org.w3c.dom.Node;
  67. import org.w3c.dom.NodeList;
  68. import org.xml.sax.XMLReader;
  69. import org.xml.sax.SAXException;
  70. /**
  71. * TestConverter is used to process a set of tests specified in
  72. * a testsuite.
  73. * This class retrieves the data in the testsuite and uses FOP
  74. * to convert the xml and xsl file into either an xml representation
  75. * of the area tree or a pdf document.
  76. * The area tree can be used for automatic comparisons between different
  77. * versions of FOP or the pdf can be view for manual checking and
  78. * pdf rendering.
  79. *
  80. * Modified by Mark Lillywhite mark-fop@inomial.com to use the new Driver
  81. * interface.
  82. */
  83. public class TestConverter extends AbstractLogEnabled {
  84. private boolean failOnly = false;
  85. private boolean outputPDF = false;
  86. private File destdir;
  87. private File compare = null;
  88. private String baseDir = "./";
  89. private Map differ = new java.util.HashMap();
  90. /**
  91. * This main method can be used to run the test converter from
  92. * the command line.
  93. * This will take a specified testsuite xml and process all
  94. * tests in it.
  95. * The command line options are:
  96. * -b to set the base directory for where the testsuite and associated files are
  97. * -failOnly to process only the tests which are specified as fail in the test results
  98. * -pdf to output the result as pdf
  99. * @param args command-line arguments
  100. */
  101. public static void main(String[] args) {
  102. if (args == null || args.length == 0) {
  103. System.out.println("test suite file name required");
  104. }
  105. TestConverter tc = new TestConverter();
  106. tc.enableLogging(new ConsoleLogger(ConsoleLogger.LEVEL_ERROR));
  107. String testFile = null;
  108. for (int count = 0; count < args.length; count++) {
  109. if (args[count].equals("-failOnly")) {
  110. tc.setFailOnly(true);
  111. } else if (args[count].equals("-pdf")) {
  112. tc.setOutputPDF(true);
  113. } else if (args[count].equals("-b")) {
  114. tc.setBaseDir(args[count + 1]);
  115. } else {
  116. testFile = args[count];
  117. }
  118. }
  119. if (testFile == null) {
  120. System.out.println("test suite file name required");
  121. }
  122. tc.runTests(testFile, "results", null);
  123. }
  124. /**
  125. * Controls whether to generate PDF or XML.
  126. * @param pdf If True, PDF is generated, Area Tree XML otherwise.
  127. */
  128. public void setOutputPDF(boolean pdf) {
  129. outputPDF = pdf;
  130. }
  131. /**
  132. * Controls whether to process only the tests which are specified as fail
  133. * in the test results.
  134. * @param fail True if only fail tests should be processed
  135. */
  136. public void setFailOnly(boolean fail) {
  137. failOnly = fail;
  138. }
  139. /**
  140. * Sets the base directory.
  141. * @param str base directory
  142. */
  143. public void setBaseDir(String str) {
  144. baseDir = str;
  145. }
  146. /**
  147. * Run the Tests.
  148. * This runs the tests specified in the xml file fname.
  149. * The document is read as a dom and each testcase is covered.
  150. * @param fname filename of the input file
  151. * @param dest destination directory
  152. * @param compDir comparison directory
  153. * @return Map a Map containing differences
  154. */
  155. public Map runTests(String fname, String dest, String compDir) {
  156. getLogger().debug("running tests in file:" + fname);
  157. try {
  158. if (compDir != null) {
  159. compare = new File(baseDir + "/" + compDir);
  160. }
  161. destdir = new File(baseDir + "/" + dest);
  162. destdir.mkdirs();
  163. File f = new File(baseDir + "/" + fname);
  164. DocumentBuilderFactory factory =
  165. DocumentBuilderFactory.newInstance();
  166. DocumentBuilder db = factory.newDocumentBuilder();
  167. Document doc = db.parse(f);
  168. NodeList suitelist = doc.getChildNodes();
  169. if (suitelist.getLength() == 0) {
  170. return differ;
  171. }
  172. Node testsuite = null;
  173. testsuite = doc.getDocumentElement();
  174. if (testsuite.hasAttributes()) {
  175. String profile =
  176. testsuite.getAttributes().getNamedItem("profile").getNodeValue();
  177. getLogger().debug("testing test suite:" + profile);
  178. }
  179. NodeList testcases = testsuite.getChildNodes();
  180. for (int count = 0; count < testcases.getLength(); count++) {
  181. Node testcase = testcases.item(count);
  182. if (testcase.getNodeName().equals("testcases")) {
  183. runTestCase(testcase);
  184. }
  185. }
  186. } catch (Exception e) {
  187. getLogger().error("Error while running tests", e);
  188. }
  189. return differ;
  190. }
  191. /**
  192. * Run a test case.
  193. * This goes through a test case in the document.
  194. * A testcase can contain a test, a result or more test cases.
  195. * A test case is handled recursively otherwise the test is run.
  196. * @param tcase Test case node to run
  197. */
  198. protected void runTestCase(Node tcase) {
  199. if (tcase.hasAttributes()) {
  200. String profile =
  201. tcase.getAttributes().getNamedItem("profile").getNodeValue();
  202. getLogger().debug("testing profile:" + profile);
  203. }
  204. NodeList cases = tcase.getChildNodes();
  205. for (int count = 0; count < cases.getLength(); count++) {
  206. Node node = cases.item(count);
  207. String nodename = node.getNodeName();
  208. if (nodename.equals("testcases")) {
  209. runTestCase(node);
  210. } else if (nodename.equals("test")) {
  211. runTest(tcase, node);
  212. } else if (nodename.equals("result")) {
  213. //nop
  214. }
  215. }
  216. }
  217. /**
  218. * Run a particular test.
  219. * This runs a test defined by the xml and xsl documents.
  220. * If the test has a result specified it is checked.
  221. * This creates an XSLTInputHandler to provide the input
  222. * for FOP and writes the data out to an XML are tree.
  223. * @param testcase Test case to run
  224. * @param test Test
  225. */
  226. protected void runTest(Node testcase, Node test) {
  227. String id = test.getAttributes().getNamedItem("id").getNodeValue();
  228. Node result = locateResult(testcase, id);
  229. boolean pass = false;
  230. if (result != null) {
  231. String agreement =
  232. result.getAttributes().getNamedItem("agreement").getNodeValue();
  233. pass = agreement.equals("full");
  234. }
  235. if (pass && failOnly) {
  236. return;
  237. }
  238. String xml = test.getAttributes().getNamedItem("xml").getNodeValue();
  239. Node xslNode = test.getAttributes().getNamedItem("xsl");
  240. String xsl = null;
  241. if (xslNode != null) {
  242. xsl = xslNode.getNodeValue();
  243. }
  244. getLogger().debug("converting xml:" + xml + " and xsl:"
  245. + xsl + " to area tree");
  246. try {
  247. File xmlFile = new File(baseDir + "/" + xml);
  248. String baseURL = null;
  249. try {
  250. baseURL = xmlFile.getParentFile().toURL().toExternalForm();
  251. } catch (Exception e) {
  252. getLogger().error("Error setting base directory");
  253. }
  254. InputHandler inputHandler = null;
  255. if (xsl == null) {
  256. inputHandler = new FOInputHandler(xmlFile);
  257. } else {
  258. inputHandler = new XSLTInputHandler(xmlFile,
  259. new File(baseDir + "/"
  260. + xsl));
  261. }
  262. XMLReader parser = inputHandler.getParser();
  263. setParserFeatures(parser);
  264. Driver driver = new Driver();
  265. setupLogger(driver, "fop");
  266. driver.initialize();
  267. FOUserAgent userAgent = new FOUserAgent();
  268. userAgent.setBaseURL(baseURL);
  269. driver.setUserAgent(userAgent);
  270. if (outputPDF) {
  271. driver.setRenderer(Driver.RENDER_PDF);
  272. } else {
  273. driver.setRenderer(Driver.RENDER_XML);
  274. }
  275. Map rendererOptions = new java.util.HashMap();
  276. rendererOptions.put("fineDetail", new Boolean(false));
  277. rendererOptions.put("consistentOutput", new Boolean(true));
  278. driver.getRenderer().setOptions(rendererOptions);
  279. driver.getRenderer().setProducer("Testsuite Converter");
  280. String outname = xmlFile.getName();
  281. if (outname.endsWith(".xml")) {
  282. outname = outname.substring(0, outname.length() - 4);
  283. }
  284. driver.setOutputStream(new java.io.BufferedOutputStream(
  285. new java.io.FileOutputStream(new File(destdir,
  286. outname + (outputPDF ? ".pdf" : ".at.xml")))));
  287. getLogger().debug("ddir:" + destdir + " on:" + outname + ".pdf");
  288. driver.render(parser, inputHandler.getInputSource());
  289. // check difference
  290. if (compare != null) {
  291. File f1 = new File(destdir, outname + ".at.xml");
  292. File f2 = new File(compare, outname + ".at.xml");
  293. if (!compareFiles(f1, f2)) {
  294. differ.put(outname + ".at.xml", new Boolean(pass));
  295. }
  296. }
  297. } catch (Exception e) {
  298. getLogger().error("Error while running tests", e);
  299. }
  300. }
  301. /**
  302. * Compare files.
  303. * @param f1 first file
  304. * @param f2 second file
  305. * @return true if equal
  306. */
  307. protected boolean compareFiles(File f1, File f2) {
  308. if (f1.length() != f2.length()) {
  309. return false;
  310. }
  311. try {
  312. InputStream is1 = new java.io.BufferedInputStream(new java.io.FileInputStream(f1));
  313. InputStream is2 = new java.io.BufferedInputStream(new java.io.FileInputStream(f2));
  314. while (true) {
  315. int ch1 = is1.read();
  316. int ch2 = is2.read();
  317. if (ch1 == ch2) {
  318. if (ch1 == -1) {
  319. return true;
  320. }
  321. } else {
  322. return false;
  323. }
  324. }
  325. } catch (Exception e) {
  326. getLogger().error("Error while comparing files", e);
  327. }
  328. return false;
  329. }
  330. private void setParserFeatures(XMLReader parser) throws FOPException {
  331. try {
  332. parser.setFeature("http://xml.org/sax/features/namespace-prefixes",
  333. true);
  334. } catch (SAXException e) {
  335. throw new FOPException("Error in setting up parser feature namespace-prefixes\n"
  336. + "You need a parser which supports SAX version 2", e);
  337. }
  338. }
  339. private Node locateResult(Node testcase, String id) {
  340. NodeList cases = testcase.getChildNodes();
  341. for (int count = 0; count < cases.getLength(); count++) {
  342. Node node = cases.item(count);
  343. String nodename = node.getNodeName();
  344. if (nodename.equals("result")) {
  345. String resultid =
  346. node.getAttributes().getNamedItem("id").getNodeValue();
  347. if (id.equals(resultid)) {
  348. return node;
  349. }
  350. }
  351. }
  352. return null;
  353. }
  354. }