/*
* Copyright (c) 2001 - 2009 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.test;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.custommonkey.xmlunit.SimpleNamespaceContext;
import org.custommonkey.xmlunit.XMLAssert;
import org.custommonkey.xmlunit.XMLUnit;
import org.custommonkey.xmlunit.XpathEngine;
import org.custommonkey.xmlunit.exceptions.XpathException;
import org.geoserver.wfs.WFSInfo;
import org.geotools.data.complex.AppSchemaDataAccess;
import org.geotools.data.complex.DataAccessRegistry;
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
/**
* Abstract base class for WFS test cases that test integration of {@link AppSchemaDataAccess} with
* GeoServer.
*
* <p>
*
* The implementation takes care to ensure that private {@link XMLUnit} namespace contexts are used
* for each mock data instance, to avoid collisions. Use of static {@link XMLAssert} methods risks
* collisions in the static namespace context. This class avoids such problems by providing its own
* instance methods like those in XMLAssert.
*
* @author Ben Caradoc-Davies, CSIRO Exploration and Mining
*/
public abstract class AbstractAppSchemaWfsTestSupport extends GeoServerAbstractTestSupport {
/**
* WFS namespaces, for use by XMLUnit. A seen in WFSTestSupport, plus xlink.
*/
@SuppressWarnings("serial")
private final Map<String, String> WFS_NAMESPACES = Collections
.unmodifiableMap(new HashMap<String, String>() {
{
put("wfs", "http://www.opengis.net/wfs");
put("ows", "http://www.opengis.net/ows");
put("ogc", "http://www.opengis.net/ogc");
put("xs", "http://www.w3.org/2001/XMLSchema");
put("xsd", "http://www.w3.org/2001/XMLSchema");
put("gml", "http://www.opengis.net/gml");
put("xlink", "http://www.w3.org/1999/xlink");
put("xsi", "http://www.w3.org/2001/XMLSchema-instance");
}
});
/**
* The XpathEngine to be used for this namespace context.
*/
private XpathEngine xpathEngine;
/**
* Subclasses override this to construct the test data.
*
* <p>
*
* Override to narrow return type and remove checked exception.
*
* @see org.geoserver.test.GeoServerAbstractTestSupport#buildTestData()
*/
@Override
protected abstract NamespaceTestData buildTestData();
/**
* Return the test data.
*
* <p>
*
* Override to narrow return type.
*
* @see org.geoserver.test.GeoServerAbstractTestSupport#getTestData()
*/
@Override
public NamespaceTestData getTestData() {
return (NamespaceTestData) super.getTestData();
}
/**
* Configure WFS to encode canonical schema location.
*
* <p>
*
* FIXME: This setting should go in wfs.xml for the mock data when tests migrated to new data
* directory format. Have to do it programmatically for now. To do this insert in wfs.xml just
* after the <tt>featureBounding</tt> setting:
*
* <p>
*
* <tt><canonicalSchemaLocation>true</canonicalSchemaLocation><tt>
*/
@Override
protected void oneTimeSetUp() throws Exception {
super.oneTimeSetUp();
WFSInfo wfs = getGeoServer().getService(WFSInfo.class);
wfs.setCanonicalSchemaLocation(true);
getGeoServer().save(wfs);
}
/**
* Unregister all data access from registry to avoid stale data access being used by other unit
* tests.
*/
@Override
protected void oneTimeTearDown() throws Exception {
super.oneTimeTearDown();
DataAccessRegistry.unregisterAll();
}
/**
* Return the response for a GET request for a path (starts with "wfs?").
*
* <p>
*
* Override to remove checked exception.
*
* @see org.geoserver.test.GeoServerAbstractTestSupport#getAsDOM(java.lang.String)
*/
@Override
protected Document getAsDOM(String path) {
try {
return super.getAsDOM(path);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Return the response for a POST request to a path (typically "wfs"). The request XML is a
* String.
*
* <p>
*
* Override to remove checked exception.
*
* @see org.geoserver.test.GeoServerAbstractTestSupport#postAsDOM(java.lang.String,
* java.lang.String)
*/
@Override
protected Document postAsDOM(String path, String xml) {
try {
return super.postAsDOM(path, xml);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Return the XpathEngine, configured for this namespace context.
*
* <p>
*
* Note that the engine is configured lazily, to ensure that the mock data has been created and
* is ready to report data namespaces, which are then put into the namespace context.
*
* @return configured XpathEngine
*/
private XpathEngine getXpathEngine() {
if (xpathEngine == null) {
xpathEngine = XMLUnit.newXpathEngine();
Map<String, String> namespaces = new HashMap<String, String>();
namespaces.putAll(WFS_NAMESPACES);
namespaces.putAll(getTestData().getNamespaces());
xpathEngine.setNamespaceContext(new SimpleNamespaceContext(namespaces));
}
return xpathEngine;
}
/**
* Return the flattened value corresponding to an XPath expression from a document.
*
* @param xpath
* XPath expression
* @param document
* the document under test
* @return flattened string value
*/
protected String evaluate(String xpath, Document document) {
try {
return getXpathEngine().evaluate(xpath, document);
} catch (XpathException e) {
throw new RuntimeException(e);
}
}
/**
* Return the list of nodes in a document that match an XPath expression.
*
* @param xpath
* XPath expression
* @param document
* the document under test
* @return list of matching nodes
*/
private NodeList getMatchingNodes(String xpath, Document document) {
try {
return getXpathEngine().getMatchingNodes(xpath, document);
} catch (XpathException e) {
throw new RuntimeException(e);
}
}
/**
* Assertion that the flattened value of an XPath expression in document is equal to the
* expected value.
*
* @param expected
* expected value of expression
* @param xpath
* XPath expression
* @param document
* the document under test
*/
protected void assertXpathEvaluatesTo(String expected, String xpath, Document document) {
assertEquals(expected, evaluate(xpath, document));
}
/**
* Assert that there are count matches of and XPath expression in a document.
*
* @param count
* expected number of matches
* @param xpath
* XPath expression
* @param document
* document under test
*/
protected void assertXpathCount(int count, String xpath, Document document) {
assertEquals(count, getMatchingNodes(xpath, document).getLength());
}
/**
* Assert that the flattened value of an XPath expression in a document matches a regular
* expression.
*
* @param regex
* regular expression that must be matched
* @param xpath
* XPath expression
* @param document
* document under test
* @throws Exception
*/
protected void assertXpathMatches(String regex, String xpath, Document document) {
assertTrue(evaluate(xpath, document).matches(regex));
}
/**
* Assert that the flattened value of an XPath expression in a document doe not match a regular
* expression.
*
* @param regex
* regular expression that must not be matched
* @param xpath
* XPath expression
* @param document
* document under test
* @throws Exception
*/
protected void assertXpathNotMatches(String regex, String xpath, Document document) {
assertFalse(evaluate(xpath, document).matches(regex));
}
/**
* Return {@link Document} as a pretty-printed string.
*
* @param document
* document to be prettified
* @return the prettified string
*/
protected String prettyString(Document document) {
OutputStream output = new ByteArrayOutputStream();
prettyPrint(document, output);
return output.toString();
}
/**
* Pretty-print a {@link Document} to an {@link OutputStream}.
*
* @param document
* document to be prettified
* @param output
* stream to which output is written
*/
protected void prettyPrint(Document document, OutputStream output) {
OutputFormat format = new OutputFormat(document);
// setIndenting must be first as it resets indent and line width
format.setIndenting(true);
format.setIndent(4);
format.setLineWidth(200);
XMLSerializer serializer = new XMLSerializer(output, format);
try {
serializer.serialize(document);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* Find the first file matching the supplied path, starting from the supplied root. This doesn't
* support multiple matching files.
*
* @param path
* Supplied path
* @param root
* Directory to start searching from
* @return Matching file
*/
protected File findFile(String path, File root) {
File target = null;
List<File> files = Arrays.asList(root.listFiles());
String[] steps = path.split("/");
for (int i = 0; i < steps.length; i++) {
for (File file : files) {
if (file.getName().equals(steps[i])) {
if (i < steps.length - 1) {
return findFile(path.substring(steps[i].length() + 1, path.length()), file);
} else {
return file;
}
}
}
}
return target;
}
}