package com.splunk.modularinput;
import com.splunk.SDKTestCase;
import org.junit.Assert;
import org.junit.Test;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.Level;
public class ModularInputTestCase {
/**
* XML parsing may introduce text nodes containing only white space between elements. These are purely formatting
* in our case, so we want to eliminate them.
*
* @param node a org.w3c.dom.Node object containing XML.
*/
public static void removeBlankTextNodes(Element node) {
NodeList children = node.getChildNodes();
// Iterate backwards through the collection since we're going to be removing elements
for (int i = children.getLength() - 1; i >= 0; i--) {
Node child = children.item(i);
if (child instanceof Text && ((Text)child).getData().trim().length() == 0) {
node.removeChild(child);
}
else if (child instanceof Element) {
removeBlankTextNodes((Element) child);
}
}
}
/**
* Assert whether two org.w3c.dom.Node objects contain identical XML, ignoring whitespace. If they do not match,
* assertXmlEqual will recursively descend through the element tree to find where they do not match.
*
* @param expected an org.w3c.dom.Node object containing the expected XML document.
* @param found an org.w3c.dom.Node object containing the XML document actually produced.
* @throws javax.xml.transform.TransformerException
* @throws javax.xml.parsers.ParserConfigurationException
*/
public void assertXmlEqual(Node expected, Node found) throws TransformerException, ParserConfigurationException {
try {
Assert.assertTrue(expected.isEqualNode(found));
} catch (AssertionError e) {
NodeList expectedChildren = expected.getChildNodes();
NodeList foundChildren = found.getChildNodes();
if (expectedChildren.getLength() != foundChildren.getLength()) {
throw new AssertionError("Expected node: \n" +
nodeToXml(expected) + "\n" +
"Generated node:\n" +
nodeToXml(found));
}
for (int i = 0; i < expectedChildren.getLength(); i++) {
assertXmlEqual(expectedChildren.item(i), foundChildren.item(i));
}
if (expected.getNodeType() == expected.TEXT_NODE && found.getNodeType() == found.TEXT_NODE) {
Assert.assertEquals(((Text)expected).getData(), ((Text)found).getData());
}
throw new AssertionError("Parents unequal but couldn't find unmatched child in node " + expected.getNodeName());
}
}
/**
* Assert whether two org.w3c.dom.Document objects contain identical XML, ignoring whitespace. If they do not match,
* assertXmlEqual will recursively descend through the element tree to find where they do not match.
*
* @param expected an org.w3c.dom.Document object containing the expected XML document.
* @param found an org.w3c.dom.Document object containing the XML document actually produced.
* @throws javax.xml.transform.TransformerException
* @throws javax.xml.parsers.ParserConfigurationException
*/
public void assertXmlEqual(Document expected, Document found) throws TransformerException, ParserConfigurationException {
removeBlankTextNodes(expected.getDocumentElement());
removeBlankTextNodes(found.getDocumentElement());
expected.normalizeDocument();
found.normalizeDocument();
assertXmlEqual((Node) expected, (Node) found);
}
/**
* Open a resource from the Splunk SDK for Java project and parse it into an org.w3c.dom.Document object.
*
* @param path a path relative to the test directory of the SDK.
* @return an org.w3c.dom.Document object containing the parsed XML.
*/
public Document resourceToXmlDocument(String path) {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setIgnoringElementContentWhitespace(true);
DocumentBuilder documentBuilder = null;
try {
documentBuilder = documentBuilderFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new AssertionError("Parser configuration failed: " + e.toString());
}
InputStream resource = SDKTestCase.openResource(path);
try {
Document doc = documentBuilder.parse(resource);
return doc;
} catch (SAXException e) {
throw new AssertionError("Could not parse XML file at " + path);
} catch (IOException e) {
throw new AssertionError("Could not read XML file at " + path);
}
}
/**
* Parse XML in a string into an org.w3c.dom.Document object.
*
* @param xml a String containing XML.
* @return an org.w3c.dom.Document object.
*/
public Document stringToXmlDocument(String xml) {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setIgnoringElementContentWhitespace(true);
DocumentBuilder documentBuilder = null;
try {
documentBuilder = documentBuilderFactory.newDocumentBuilder();
} catch (ParserConfigurationException e) {
throw new AssertionError("Could not configure parser: " + e.toString());
}
Document generatedDoc = null;
try {
generatedDoc = documentBuilder.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
return generatedDoc;
} catch (SAXException e) {
e.printStackTrace();
throw new AssertionError("Error parsing XML passed to function: " + e.toString());
} catch (IOException e) {
e.printStackTrace();
throw new AssertionError("Error reading XML passed to function: " + e.toString());
}
}
/**
* Transform the given org.w3c.dom.Node object into a String containing the corresponding XML.
*
* This function is primarily for showing sensible error messages.
*
* @param node the org.w3c.dom.Node object to serialize.
* @return a String containing generated XML.
* @throws TransformerException
* @throws ParserConfigurationException
*/
public String nodeToXml(Node node) throws TransformerException, ParserConfigurationException {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.VERSION, "1.0");
transformer.setOutputProperty(OutputKeys.STANDALONE, "yes");
Document doc = node.getOwnerDocument();
DOMSource source = new DOMSource(doc);
StringWriter buffer = new StringWriter();
StreamResult result = new StreamResult(buffer);
transformer.transform(source, result);
String xml = buffer.toString();
return xml;
}
/**
* Tries the known cases supported by XmlUtil.normalizeBoolean, and asserts that it throws an error
* when passed an unknown case.
*/
@Test
public void testNormalizeBoolean() throws MalformedDataException {
String[] trueValues = new String[] {"true", "t", "TRUE ", "y", " YeS", "1", "ON"};
String[] falseValues = new String[] {"false", "f", "FALSE ", " oFF", "no", "0", "n"};
String[] invalidValues = new String[] {null, "boris", "fal"};
for (String s : trueValues) {
Assert.assertTrue(XmlUtil.normalizeBoolean(s));
}
for (String s : falseValues) {
Assert.assertFalse(XmlUtil.normalizeBoolean(s));
}
boolean fail;
for (String s : invalidValues) {
fail = true;
try {
XmlUtil.normalizeBoolean(null);
} catch (MalformedDataException e) {
fail = false;
} finally {
Assert.assertFalse(fail);
}
}
}
/**
* Test the methods on SingleValueParameter to coerce its value to a boolean or various kinds of numbers.
*/
@Test
public void testCoercionMethods() throws MalformedDataException {
Assert.assertEquals(true, new SingleValueParameter("name", "TRuE ").getBoolean());
Assert.assertEquals(5, new SingleValueParameter("name", "5").getInt());
Assert.assertEquals(27, new SingleValueParameter("name", "27").getLong());
Assert.assertEquals(5.2, new SingleValueParameter("name", "5.2").getFloat(), 1e-6);
Assert.assertEquals(5.2, new SingleValueParameter("name", "5.2").getDouble(), 1e-6);
}
}