1
2
3
4
5
6
7
8
9
10
11 package org.astrogrid.test;
12
13 import org.astrogrid.io.Piper;
14 import org.astrogrid.util.DomHelper;
15
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
18 import org.custommonkey.xmlunit.Validator;
19 import org.custommonkey.xmlunit.XMLAssert;
20 import org.w3c.dom.Document;
21 import org.w3c.dom.Element;
22 import org.xml.sax.InputSource;
23 import org.xml.sax.SAXException;
24 import org.xml.sax.SAXNotRecognizedException;
25 import org.xml.sax.SAXNotSupportedException;
26 import org.xml.sax.SAXParseException;
27 import org.xml.sax.XMLReader;
28 import org.xml.sax.helpers.DefaultHandler;
29 import org.xml.sax.helpers.XMLReaderFactory;
30
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.io.InputStreamReader;
34 import java.io.StringReader;
35 import java.io.StringWriter;
36 import java.net.URL;
37 import java.util.Iterator;
38 import java.util.Map;
39
40 import javax.xml.parsers.ParserConfigurationException;
41
42 import junit.framework.Assert;
43 import junit.framework.AssertionFailedError;
44
45 /*** class of static JUnit assertion methods, for asserting things relevant to astrogrid.
46 * extends XMLAssert - which makes assertions about xml documents -
47 * this class builds upon this to check properties of votables, and assertions to validate against DTD and schema.
48 * @author Noel Winstanley nw@jb.man.ac.uk 27-Aug-2004
49 *
50 */
51 public class AstrogridAssert extends XMLAssert{
52 /***
53 * Commons Logger for this class
54 */
55 private static final Log logger = LogFactory.getLog(AstrogridAssert.class);
56 /*** Construct a new XMLAssertions
57 *
58 */
59 private AstrogridAssert() {
60 super();
61 }
62
63 /*** static initializer - finds path to votable dtd.*/
64 static {
65 VOTABLE_SYSTEM_ID = AstrogridAssert.class.getResource("VOTable.dtd");
66 }
67 /*** system id for votable dtd - i.e. absolute location on classpath of votable dtd */
68 public static URL VOTABLE_SYSTEM_ID;
69 /*** doctype for votable */
70 public static final String VOTABLE_DOCTYPE = "VOTABLE";
71
72
73 /*** assert is a votable document
74 * @param s string containing xml.
75 *
76 */
77 public static void assertVotable(String s) {
78 assertDTDValid(s,VOTABLE_DOCTYPE,VOTABLE_SYSTEM_ID);
79 }
80 /*** assert is a votable document
81 * @param is input stream containing xml. */
82 public static void assertVotable(InputStream is) {
83 assertDTDValid(is,VOTABLE_DOCTYPE,VOTABLE_SYSTEM_ID);
84 }
85
86 /*** assert is a votable document
87 * @param d suspected votable.
88 * @asserts doctype, and dtd validates */
89 public static void assertVotable(Document d) {
90 assertDTDValid(d,VOTABLE_DOCTYPE,VOTABLE_SYSTEM_ID);
91 }
92 /*** assert is a votable document
93 * @param e suspected votable.
94 * @asserts doctype, and dtd validates */
95 public static void assertVotable(Element e) {
96 assertDTDValid(e,VOTABLE_DOCTYPE,VOTABLE_SYSTEM_ID);
97 }
98
99
100
101 /*** assert xml is DTD-valid.
102 * @param xml string containing xml
103 * @param doctype the DOCTYPE the xml should have (i.e. root element).
104 * @param dtdResource url of dtd file.
105 *
106 */
107 public static void assertDTDValid(String xml,String doctype, URL dtdResource) {
108 try {
109 assertXMLValid(xml,dtdResource.toString(),doctype);
110 } catch (SAXException e) {
111 fail("assertDTDValid: parse failure " + e.getMessage());
112 } catch (ParserConfigurationException e) {
113 fail("assertDTDValid: parser configuration failure " + e.getMessage());
114 }
115 }
116
117 /*** assert xml is DTD-valid
118 * @param d document to validate
119 * @param doctype the DOCTYPE that the xml should have (i.e. the root element).
120 * @param dtdResource url of dtd file.
121 */
122 public static void assertDTDValid(Document d,String doctype, URL dtdResource) {
123 try {
124 assertXMLValid(new Validator(d,dtdResource.toString(),doctype));
125 } catch (SAXException e) {
126 fail("assertDTDValid: parse failure " + e.getMessage());
127 } catch (ParserConfigurationException e) {
128 fail("assertDTDValid: parser configuration failure " + e.getMessage());
129 }
130 }
131
132 /*** assert xml is DTD valid.
133 * @param xmlStream stream of xml to validate
134 * @param doctype the DOCTYPE that the xml should have (ie. the expected root element)
135 * @param dtdResource url to dtd file.
136 */
137 public static void assertDTDValid(InputStream xmlStream, String doctype, URL dtdResource) {
138 try {
139 assertXMLValid(new Validator(DomHelper.newDocument(xmlStream),dtdResource.toString(),doctype));
140 } catch (SAXException e) {
141 fail("assertDTDValid: parse failure " + e.getMessage());
142 } catch (ParserConfigurationException e) {
143 fail("assertDTDValid: parser configuration failure " + e.getMessage());
144 } catch (IOException e) {
145 fail("assertDTDValid: failed to read from stream " + e.getMessage());
146 }
147 }
148
149 /*** assert xml is DTD valid
150 * @param e xml to validate
151 * @param doctype the DOCTYPE that the xml should have (ie. the expected root element)
152 * @param dtdResource url to dtd file.
153 */
154 public static void assertDTDValid(Element e,String doctype,URL dtdResource) {
155 try {
156 assertXMLValid(DomHelper.ElementToString(e),dtdResource.toString(),doctype);
157 } catch (SAXException e1) {
158 fail("assertDTDValid: parse failure " + e1.getMessage());
159 } catch (ParserConfigurationException e1) {
160 fail("assertDTDValid: parser configuration failure " + e1.getMessage());
161 }
162 }
163
164 /*** assert xml is schema valid.
165 * @param xml string containing xml to validate.
166 * @param rootElementName expected localname of the root element of the document
167 * @param schemaLocations map of namespace - schema location associations.<br>
168 * keys of map expected to be namespace (will be converted to java.lang.String via <tt>toString()</tt>), <br>
169 * values of map expected to be url locations of schema document (will be converted to java.lang.String via <tt>toString()</tt>)
170 *
171 * @see org.astrogrid.test.schema.SchemaMap in workflow-objects project
172 */
173 public static void assertSchemaValid(String xml,String rootElementName,Map schemaLocations) {
174 try {
175 assertXpathEvaluatesTo(rootElementName,"local-name(/*)",xml);
176 } catch (Exception e) {
177 fail("Failed to extract root element name " + e.getMessage());
178 }
179 AstrogridAssertDefaultHandler handler = new AstrogridAssertDefaultHandler();
180 XMLReader parser = createParser(handler,schemaLocations);
181 InputSource source = new InputSource(new StringReader(xml));
182 try {
183 parser.parse(source);
184 } catch (Exception e) {
185 fail("failed to validate against schema: " + e.getMessage() + handler.getMessages());
186 }
187
188 }
189
190 /***assert xml is schema valid.
191 * @param d document to validate
192 * @param rootElementName expected localname of the root element of the document
193 * @param schemaLocations map of namespace - schema location associations.<br>
194 * keys of map expected to be namespace (will be converted to java.lang.String via <tt>toString()</tt>), <br>
195 * values of map expected to be url locations of schema document (will be converted to java.lang.String via <tt>toString()</tt>)
196 * @see org.astrogrid.test.schema.SchemaMap in workflow-objects project *
197 */
198 public static void assertSchemaValid(Document d,String rootElementName,Map schemaLocations) {
199 assertSchemaValid(DomHelper.DocumentToString(d),rootElementName,schemaLocations);
200 }
201
202 /***assert xml is schema valid.
203 * @param is stream of xml to validate
204 * @param rootElementName expected localname of the root element of the document
205 * @param schemaLocations map of namespace - schema location associations.<br>
206 * keys of map expected to be namespace (will be converted to java.lang.String via <tt>toString()</tt>), <br>
207 * values of map expected to be url locations of schema document (will be converted to java.lang.String via <tt>toString()</tt>)
208 * * @see org.astrogrid.test.schema.SchemaMap in workflow-objects project
209 */
210 public static void assertSchemaValid(InputStream is,String rootElementName,Map schemaLocations){
211
212 try {
213 assertSchemaValid(getStreamContents(is),rootElementName,schemaLocations);
214 } catch (IOException e) {
215 fail("Failed to read from stream " + e.getMessage());
216 }
217 }
218
219
220 /***assert xml is schema valid.
221 * @param e element to validate
222 * @param rootElementName expected localname of the root element of the document
223 * @param schemaLocations map of namespace - schema location associations.<br>
224 * keys of map expected to be namespace (will be converted to java.lang.String via <tt>toString()</tt>), <br>
225 * values of map expected to be url locations of schema document (will be converted to java.lang.String via <tt>toString()</tt>)
226 * @see org.astrogrid.test.schema.SchemaMap in workflow-objects project *
227 */
228 public static void assertSchemaValid(Element e,String rootElementName,Map schemaLocations) {
229 assertSchemaValid(DomHelper.ElementToString(e),rootElementName,schemaLocations);
230 }
231
232
233
234
235 /*** create an xml parser, setup to validate using schema, register
236 * appropriate schema locations, and an error handler
237 * @param handler - handler to use
238 * @param schemaLocations
239 * @return
240 */
241 private static XMLReader createParser(DefaultHandler handler,Map schemaLocations) {
242
243 String locationString = mkSchemaLocationString(schemaLocations);
244 try {
245
246 XMLReader reader = XMLReaderFactory.createXMLReader("org.apache.xerces.parsers.SAXParser");
247 reader.setFeature("http://xml.org/sax/features/validation",true);
248 reader.setFeature("http://apache.org/xml/features/validation/schema",true);
249 reader.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation",locationString);
250 reader.setErrorHandler(handler);
251 reader.setContentHandler(handler);
252 return reader;
253 } catch (SAXNotRecognizedException e) {
254 fail("required features not rcognized by this xml parser " + e.getMessage());
255 } catch (SAXNotSupportedException e) {
256 fail("required features not supported by this xml parser " + e.getMessage());
257 } catch (SAXException e) {
258 fail("cannot create xml reader " + e.getMessage());
259 }
260 throw new IllegalStateException("Cannot be reached");
261
262
263 }
264 /*** collapse the map into a space-separated string, suitable for passing to the parser */
265 private static String mkSchemaLocationString(Map schemaLocations) {
266 StringBuffer result = new StringBuffer();
267 for (Iterator i = schemaLocations.entrySet().iterator(); i.hasNext(); ) {
268 Map.Entry e = (Map.Entry)i.next();
269 result.append(e.getKey().toString());
270 result.append(' ');
271 result.append(e.getValue().toString());
272 result.append(' ');
273 }
274 return result.toString();
275 }
276
277
278 /*** assert that input is well formed xml
279 * @param xmlDocument document to check for wellformedness.
280 *
281 */
282 public static void assertWellFormedXML(String xmlDocument) {
283 try {
284 assertNotNull("null document returned",DomHelper.newDocument(xmlDocument));
285 } catch (ParserConfigurationException e) {
286 Assert.fail("Problem with parser:" + e.getMessage());
287 } catch (SAXException e) {
288 Assert.fail("xmlDocument is not well formed:" + e.getMessage());
289 } catch (IOException e) {
290 Assert.fail("Problem reading document:" + e.getMessage());
291 }
292 }
293 /*** assert that input is well formed xml
294 * @param xmlStream stream of content to check.
295 */
296 public static void assertWellFormedXML(InputStream xmlStream) {
297 try {
298 assertNotNull("null document returned",DomHelper.newDocument(xmlStream));
299 } catch (ParserConfigurationException e) {
300 Assert.fail("Problem with parser:" + e.getMessage());
301 } catch (SAXException e) {
302 Assert.fail("xmlDocument is not well formed:" + e.getMessage());
303 } catch (IOException e) {
304 Assert.fail("Problem reading document:" + e.getMessage());
305 }
306 }
307
308
309
310 /***
311 * load a resource into a string.
312 * @asserts resource can be found, and is not null
313 * @param c class that resorce path is relative to
314 * @param resource path to resurce
315 * @return comtents of this resource.
316 * @throws IOException
317 */
318 public static String getResourceAsString(Class c, String resource) throws IOException {
319 InputStream is = c.getResourceAsStream(resource);
320 String script = AstrogridAssert.getStreamContents(is);
321 assertNotNull(script);
322 return script;
323 }
324
325 /*** read stream contents into a string */
326 public static String getStreamContents(InputStream is) throws IOException {
327 assertNotNull(is);
328 StringWriter sw = new StringWriter();
329 Piper.bufferedPipe(new InputStreamReader(is), sw);
330 return sw.toString();
331 }
332 /*** handler passed to schema-validation parses - logs all errors, throws assertion failed if errors seen by end */
333 static class AstrogridAssertDefaultHandler extends DefaultHandler {
334 private StringBuffer buff = new StringBuffer();
335
336 public String getMessages() {
337 return buff.toString();
338 }
339
340 boolean sawError = false;
341
342 public void endDocument() throws SAXException {
343 if (sawError) {
344 throw new AssertionFailedError("The document is invalid with respect to its schema"
345 + this.getMessages());
346 }
347 }
348
349 public void error(SAXParseException e) throws SAXException {
350 sawError = true;
351 System.err.println("Error:" + this.getMessage(e));
352 this.buff.append("\n").append(this.getMessage(e));
353 }
354
355 public void fatalError(SAXParseException e) throws SAXException {
356 sawError = true;
357 System.err.println("Fatal: " + this.getMessage(e));
358 this.buff.append("\n").append(this.getMessage(e));
359 }
360
361 public void warning(SAXParseException e) throws SAXException {
362 System.err.println("Warn: " + this.getMessage(e));
363 this.buff.append("\n").append(this.getMessage(e));
364 }
365
366 private String getMessage(SAXParseException e) {
367 return e.getMessage() +
368 " Found at line " +
369 e.getLineNumber() +
370 ", column " +
371 e.getColumnNumber() +
372 " in " +
373 e.getSystemId();
374 }
375 }
376
377 }
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417