/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* $Id$ */ package org.apache.fop.tools; import java.io.File; import java.io.OutputStream; import java.net.URI; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.apache.commons.io.IOUtils; import org.apache.commons.logging.impl.SimpleLog; import org.apache.fop.apps.FOUserAgent; import org.apache.fop.apps.FopFactory; import org.apache.fop.apps.MimeConstants; import org.apache.fop.cli.InputHandler; import org.apache.fop.tools.anttasks.FileCompare; /** * TestConverter is used to process a set of tests specified in * a testsuite. * This class retrieves the data in the testsuite and uses FOP * to convert the xml and xsl file into either an xml representation * of the area tree or a pdf document. * The area tree can be used for automatic comparisons between different * versions of FOP or the pdf can be view for manual checking and * pdf rendering. */ public class TestConverter { private boolean failOnly; private String outputFormat = MimeConstants.MIME_FOP_AREA_TREE; private File destdir; private File compare; private String baseDir = "./"; private Map differ = new java.util.HashMap(); /** * logging instance */ protected SimpleLog logger; /** * This main method can be used to run the test converter from * the command line. * This will take a specified testsuite xml and process all * tests in it. * The command line options are: * -b to set the base directory for where the testsuite and associated files are * -failOnly to process only the tests which are specified as fail in the test results * -pdf to output the result as pdf * @param args command-line arguments */ public static void main(String[] args) { if (args == null || args.length == 0) { System.out.println("test suite file name required"); return; } TestConverter tc = new TestConverter(); String results = "results"; String testFile = null; for (int count = 0; count < args.length; count++) { if (args[count].equals("-failOnly")) { tc.setFailOnly(true); } else if (args[count].equals("-pdf")) { tc.setOutputFormat(MimeConstants.MIME_PDF); } else if (args[count].equals("-rtf")) { tc.setOutputFormat(MimeConstants.MIME_RTF); } else if (args[count].equals("-ps")) { tc.setOutputFormat(MimeConstants.MIME_POSTSCRIPT); } else if (args[count].equals("-d")) { tc.setDebug(true); } else if (args[count].equals("-b")) { tc.setBaseDir(args[++count]); } else if (args[count].equals("-results")) { results = args[++count]; } else { testFile = args[count]; } } if (testFile == null) { System.out.println("test suite file name required"); } tc.runTests(testFile, results, null); } /** * Construct a new TestConverter */ public TestConverter() { logger = new SimpleLog("FOP/Test"); logger.setLevel(SimpleLog.LOG_LEVEL_ERROR); } /** * Controls output format to generate * @param outputFormat the MIME type of the output format */ public void setOutputFormat(String outputFormat) { this.outputFormat = outputFormat; } /** * Controls whether to process only the tests which are specified as fail * in the test results. * @param fail True if only fail tests should be processed */ public void setFailOnly(boolean fail) { failOnly = fail; } /** * Sets the base directory. * @param str base directory */ public void setBaseDir(String str) { baseDir = str; } /** * Controls whether to set logging to debug level * @param debug If true, debug level, if false, error level */ public void setDebug(boolean debug) { if (debug) { logger.setLevel(SimpleLog.LOG_LEVEL_DEBUG); } else { logger.setLevel(SimpleLog.LOG_LEVEL_ERROR); } } /** * Run the Tests. * This runs the tests specified in the xml file fname. * The document is read as a dom and each testcase is covered. * @param fname filename of the input file * @param dest destination directory * @param compDir comparison directory * @return Map a Map containing differences */ public Map runTests(String fname, String dest, String compDir) { logger.debug("running tests in file:" + fname); try { if (compDir != null) { compare = new File(baseDir + "/" + compDir); } destdir = new File(baseDir + "/" + dest); destdir.mkdirs(); File f = new File(baseDir + "/" + fname); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder db = factory.newDocumentBuilder(); Document doc = db.parse(f); NodeList suitelist = doc.getChildNodes(); if (suitelist.getLength() == 0) { return differ; } Node testsuite = null; testsuite = doc.getDocumentElement(); if (testsuite.hasAttributes()) { String profile = testsuite.getAttributes().getNamedItem("profile").getNodeValue(); logger.debug("testing test suite:" + profile); } NodeList testcases = testsuite.getChildNodes(); for (int count = 0; count < testcases.getLength(); count++) { Node testcase = testcases.item(count); if (testcase.getNodeName().equals("testcases")) { runTestCase(testcase); } } } catch (Exception e) { logger.error("Error while running tests", e); } return differ; } /** * Run a test case. * This goes through a test case in the document. * A testcase can contain a test, a result or more test cases. * A test case is handled recursively otherwise the test is run. * @param tcase Test case node to run */ protected void runTestCase(Node tcase) { if (tcase.hasAttributes()) { String profile = tcase.getAttributes().getNamedItem("profile").getNodeValue(); logger.debug("testing profile:" + profile); } NodeList cases = tcase.getChildNodes(); for (int count = 0; count < cases.getLength(); count++) { Node node = cases.item(count); String nodename = node.getNodeName(); if (nodename.equals("testcases")) { runTestCase(node); } else if (nodename.equals("test")) { runTest(tcase, node); } /* else if (nodename.equals("result")) { //nop } */ } } /** * Run a particular test. * This runs a test defined by the xml and xsl documents. * If the test has a result specified it is checked. * This creates an XSLTInputHandler to provide the input * for FOP and writes the data out to an XML are tree. * @param testcase Test case to run * @param test Test */ protected void runTest(Node testcase, Node test) { String id = test.getAttributes().getNamedItem("id").getNodeValue(); Node result = locateResult(testcase, id); boolean pass = false; if (result != null) { String agreement = result.getAttributes().getNamedItem("agreement").getNodeValue(); pass = agreement.equals("full"); } if (pass && failOnly) { return; } String xml = test.getAttributes().getNamedItem("xml").getNodeValue(); Node xslNode = test.getAttributes().getNamedItem("xsl"); String xsl = null; if (xslNode != null) { xsl = xslNode.getNodeValue(); } logger.debug("converting xml:" + xml + " and xsl:" + xsl + " to area tree"); String res = xml; Node resNode = test.getAttributes().getNamedItem("results"); if (resNode != null) { res = resNode.getNodeValue(); } try { File xmlFile = new File(baseDir + "/" + xml); URI baseUri = xmlFile.getParentFile().toURI(); InputHandler inputHandler = null; if (xsl == null) { inputHandler = new InputHandler(xmlFile); } else { inputHandler = new InputHandler(xmlFile, new File(baseDir + "/" + xsl), null); } FopFactory fopFactory = FopFactory.newInstance(baseUri); FOUserAgent userAgent = fopFactory.newFOUserAgent(); userAgent.getRendererOptions().put("fineDetail", false); userAgent.getRendererOptions().put("consistentOutput", true); userAgent.setProducer("Testsuite Converter"); String outname = res; if (outname.endsWith(".xml") || outname.endsWith(".pdf")) { outname = outname.substring(0, outname.length() - 4); } File outputFile = new File(destdir, outname + makeResultExtension()); outputFile.getParentFile().mkdirs(); OutputStream outStream = null; try { outStream = new java.io.BufferedOutputStream( new java.io.FileOutputStream(outputFile)); logger.debug("ddir:" + destdir + " on:" + outputFile.getName()); inputHandler.renderTo(userAgent, outputFormat, outStream); } finally { IOUtils.closeQuietly(outStream); } // check difference if (compare != null) { File f1 = new File(destdir, outname + ".at.xml"); File f2 = new File(compare, outname + ".at.xml"); if (!compareFiles(f1, f2)) { differ.put(outname + ".at.xml", pass); } } } catch (Exception e) { logger.error("Error while running tests", e); } } /** * Return a suitable file extension for the output format. */ private String makeResultExtension() { if (MimeConstants.MIME_PDF.equals(outputFormat)) { return ".pdf"; } else if (MimeConstants.MIME_RTF.equals(outputFormat)) { return ".rtf"; } else if (MimeConstants.MIME_POSTSCRIPT.equals(outputFormat)) { return ".ps"; } else { return ".at.xml"; } } /** * Compare files. * @param f1 first file * @param f2 second file * @return true if equal */ protected boolean compareFiles(File f1, File f2) { try { return FileCompare.compareFiles(f1, f2); } catch (Exception e) { logger.error("Error while comparing files", e); return false; } } private Node locateResult(Node testcase, String id) { NodeList cases = testcase.getChildNodes(); for (int count = 0; count < cases.getLength(); count++) { Node node = cases.item(count); String nodename = node.getNodeName(); if (nodename.equals("result")) { String resultid = node.getAttributes().getNamedItem("id").getNodeValue(); if (id.equals(resultid)) { return node; } } } return null; } }