Source code: org/esau/ptarmigan/impl/test/PtarmiganTest.java
1 /* $Header: /cvsroot/ptarmigan/ptarmigan/src/java/org/esau/ptarmigan/impl/test/PtarmiganTest.java,v 1.4 2002/09/24 02:51:47 reedesau Exp $ */
2
3 package org.esau.ptarmigan.impl.test;
4
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.BufferedInputStream;
9 import java.io.FileReader;
10 import java.io.IOException;
11 import java.io.InputStream;
12 import java.io.OutputStreamWriter;
13 import java.io.Reader;
14 import java.io.Writer;
15 import javax.xml.parsers.ParserConfigurationException;
16
17 import junit.framework.Test;
18 import junit.framework.TestCase;
19 import junit.framework.TestSuite;
20 import junit.framework.Assert;
21 import org.custommonkey.xmlunit.Diff;
22
23 import org.xml.sax.SAXException;
24 import org.xml.sax.InputSource;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 import org.jdom.Attribute;
30 import org.jdom.Document;
31 import org.jdom.Element;
32 import org.jdom.JDOMException;
33 import org.jdom.Namespace;
34 import org.jdom.input.SAXBuilder;
35 import org.jdom.output.XMLOutputter;
36
37 import org.esau.ptarmigan.Generator;
38 import org.esau.ptarmigan.GeneratorFactory;
39 import org.esau.ptarmigan.util.HelperIO;
40 import org.esau.ptarmigan.util.HelperURL;
41
42 /**
43 * Test Suite for the default implementation of Ptarmigan
44 * <p>
45 * This suite presently relies on JDOM to (1) build documents from
46 * SAX events and (2) output XML.
47 * <p>
48 * This suite presently relies on xmlunit.Diff to detect parsing
49 * problems.
50 * <p>
51 * TODO: how to handle different property/feature combinations?
52 * Different directories for test files?
53 * Something in the filename?
54 *
55 * @author Reed Esau
56 */
57 public class PtarmiganTest extends TestCase {
58
59 /** this will do schema validation */
60 static final String SAX_PARSER_XERCES = "org.apache.xerces.parsers.SAXParser";
61
62 String m_test_input_dir;
63 String m_test_control_dir;
64 String m_test_output_dir;
65
66 boolean m_recursive; // if true, will drill down on input dir
67 boolean m_validate; // if true, feeds generated xml to validating parser
68 boolean m_verify; // if true, diffs control vs forecasted
69 boolean m_incl_digest; // if true, a digest will be calculated against the data range of each file
70 boolean m_incl_playlist_entries;
71
72 public PtarmiganTest(String name) {
73 super(name);
74
75 m_test_input_dir = System.getProperty("test.input.dir" );
76 m_test_control_dir = System.getProperty("test.control.dir");
77 m_test_output_dir = System.getProperty("test.output.dir" );
78 m_recursive = getPropertyBool("test.recursive" , false);
79 m_validate = getPropertyBool("test.validate" , true );
80 m_verify = getPropertyBool("test.verify" , false);
81 m_incl_digest = getPropertyBool("test.incl_digest" , false);
82 m_incl_playlist_entries = getPropertyBool("test.incl_playlist_entries", true);
83
84 log.debug("m_test_input_dir =" + m_test_input_dir );
85 log.debug("m_test_control_dir=" + m_test_control_dir);
86 log.debug("m_test_output_dir =" + m_test_output_dir );
87 log.debug("m_recursive =" + m_recursive );
88 log.debug("m_validate =" + m_validate );
89 log.debug("m_verify =" + m_verify );
90 log.debug("m_incl_digest =" + m_incl_digest );
91 log.debug("m_incl_playlist_entries =" + m_incl_playlist_entries );
92 }
93
94
95 public static Test suite() {
96 return new TestSuite(PtarmiganTest.class);
97 }
98
99
100 /** run generator against all files specified in property "test.input.dir" */
101 public void testAll() {
102 try {
103 File dir = new File(m_test_input_dir);
104 parseDirectory(dir);
105 log.debug("done");
106 }
107 catch (Exception e) {
108 log.warn(e.getMessage());
109 e.printStackTrace();
110 Assert.fail();
111 }
112 }
113
114 /** run generator against all files in specified directory */
115 void parseDirectory(File dir) throws Exception {
116 File[] files = dir.listFiles();
117 if (files == null) {
118 log.warn("parseDirectory: not found: " + dir);
119 return;
120 }
121 for (int i = 0; i < files.length; i++) {
122 File f = files[i];
123 if (f.isDirectory()) {
124 if (m_recursive && f.getName().startsWith(".")==false)
125 parseDirectory(f); // recurse into child directories
126 }
127 else if (f.exists())
128 parseFile(f);
129 else
130 log.warn("parseDirectory: the file does not exist: " + f);
131 }
132 }
133
134 /** run generator against one file */
135 void parseFile(File infile) throws Exception {
136
137 log.debug("parseFile: infile=" + infile);
138
139 Document doc = buildDocument(infile); // parse to JDOM
140
141 // output JDOM to XML
142 File outfile = new File(m_test_output_dir, infile.getName() + ".xml");
143 writeDocument(doc, outfile);
144
145 if (m_validate) {
146 // because '#' in File objects cause trouble for JDOM/XML parser,
147 // we're converting to a system_id (i.e., a url)
148 String system_id = HelperURL.fileToSystemId(outfile);
149 build(system_id, true); // validate that XML
150 }
151
152 // and verify XML against the control
153 if (m_verify) {
154 File control_file = new File(m_test_control_dir, infile.getName() + ".xml");
155 verify(control_file, outfile);
156 }
157 }
158
159 /**
160 * Build a JDOM document from a media file source using SAX and our own
161 * parser. <P> Note that we do the dual assignment of InputStream/system_id
162 * to the InputSource. This is because we can be sure that the system-id
163 * will be properly resolved to a filename, so we open it ourselves. The
164 * system-id is provided so that the Ptarmigan generator can extract details
165 * from the file for output as SAX events.
166 */
167 Document buildDocument(File infile) throws Exception {
168
169 Generator generator = GeneratorFactory.newInstance();
170
171 //int buf_size = 0;
172 int buf_size = 8192;
173
174 generator.setFeature(Generator.FEATURE_INCLUDE_DIGEST, m_incl_digest);
175 generator.setFeature(Generator.FEATURE_INCLUDE_PLAYLIST_ENTRIES,
176 m_incl_playlist_entries);
177
178 if (buf_size > 0)
179 generator.setProperty(Generator.PROPERTY_READ_LIMIT, new Integer(buf_size));
180
181 SAXBuilder builder = new SAXBuilder();
182 builder.setXMLFilter(generator);
183
184 InputStream is = null;
185 try {
186 is = new FileInputStream(infile);
187
188 if (buf_size > 0)
189 is = new BufferedInputStream(is, buf_size);
190
191 InputSource input_source = new InputSource(is);
192
193 String system_id = HelperURL.fileToSystemId(infile);
194 input_source.setSystemId(system_id);
195 log.debug("system_id=" + system_id);
196
197 Document doc = builder.build(input_source); // the heavy lifting!
198
199 assignNamespace( doc.getRootElement() );
200
201 return doc;
202 }
203 finally {
204 HelperIO.safeClose(is);
205 }
206 }
207
208 /** */
209 void assignNamespace(Element root) throws IOException {
210
211 String pt_uri = "http://esau.org/ns/ptarmigan"; //TODO: get dynamically from GeneratorImpl
212 String pt_xsd = "schema/ptarmigan.xsd";
213
214 // convert the relative path to a URL for schemaLocation
215 String url_xsd = new File(pt_xsd).toURL().toExternalForm();
216
217 Namespace ns_xsi = Namespace.getNamespace("xsi",
218 "http://www.w3.org/2001/XMLSchema-instance");
219 root.addNamespaceDeclaration(ns_xsi);
220
221 Attribute attr = new Attribute("schemaLocation",
222 pt_uri+" "+url_xsd,
223 ns_xsi);
224 root.setAttribute(attr);
225 }
226
227 /** output a JDOM document as XML */
228 void writeDocument(Document doc, File file) throws IOException, JDOMException {
229 OutputStreamWriter writer = null;
230 try {
231 writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
232 writeDocument(doc, writer);
233 }
234 finally {
235 HelperIO.safeClose(writer);
236 }
237 }
238
239
240 /** output a JDOM document as XML */
241 void writeDocument(Document doc, Writer writer) throws IOException, JDOMException {
242 XMLOutputter op = new XMLOutputter("\t", true);
243 op.output(doc, writer);
244 }
245
246
247 /**
248 * build a JDOM document, optionall using the validation machinery in the
249 * SAX XML parser to validate the document
250 */
251 Document build(String system_id, boolean validate) throws IOException, JDOMException {
252 SAXBuilder builder;
253 if (validate) {
254 builder = new SAXBuilder(SAX_PARSER_XERCES, true);
255 builder.setFeature("http://apache.org/xml/features/validation/schema", true);
256 }
257 else
258 builder = new SAXBuilder(); // no validate
259
260 log.info("building " + system_id);
261 return builder.build(system_id); //NOTE: don't use File as '#' chars cause truncation
262 }
263
264
265 /**
266 * Compare two XML files and fail the TestCase if there are any differences.
267 */
268 void verify(File file1, File file2) throws IOException, SAXException, ParserConfigurationException {
269 Reader reader1 = null;
270 Reader reader2 = null;
271 try {
272 reader1 = new FileReader(file1);
273 reader2 = new FileReader(file2);
274 verify(reader1, reader2);
275 }
276 finally {
277 HelperIO.safeClose(reader1);
278 HelperIO.safeClose(reader2);
279 }
280 }
281
282 /**
283 * Compare two XML readers and fail the TestCase if there are any differences.
284 */
285 void verify(Reader reader1, Reader reader2)
286 throws IOException, SAXException, ParserConfigurationException {
287
288 Diff diff = new Diff(reader1, reader2);
289
290 if (diff.identical() == false) {
291 StringBuffer buf = new StringBuffer("Difference found: ");
292 diff.appendMessage(buf);
293 log.debug(buf.toString());
294 Assert.fail();
295 }
296 }
297
298 //
299 // helpers
300 //
301
302 boolean getPropertyBool(String name, boolean def_value) {
303 String value = System.getProperty(name);
304 if (value == null)
305 return def_value;
306 return value.equalsIgnoreCase("true");
307 }
308
309 /**
310 * logging object
311 */
312 static Log log = LogFactory.getLog(PtarmiganTest.class);
313 }
314 /*
315 PTARMIGAN MODIFIED BSD LICENSE
316
317 Copyright (c) 2002, Reed Esau (reed.esau@pobox.com) All rights reserved.
318
319 Redistribution and use in source and binary forms, with or without
320 modification, are permitted provided that the following conditions are
321 met:
322
323 Redistributions of source code must retain the above copyright notice,
324 this list of conditions and the following disclaimer.
325
326 Redistributions in binary form must reproduce the above copyright notice,
327 this list of conditions and the following disclaimer in the documentation
328 and/or other materials provided with the distribution.
329
330 Neither the name of the Ptarmigan Project
331 (http://ptarmigan.sourceforge.net) nor the names of its contributors may
332 be used to endorse or promote products derived from this software without
333 specific prior written permission.
334
335 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
336 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
337 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
338 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
339 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
340 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
341 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
342 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
343 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
344 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
345 POSSIBILITY OF SUCH DAMAGE.
346 */