/** * Copyright (c) Codice Foundation * <p/> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p/> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. **/ package ddf.catalog.transform.xml; import static org.custommonkey.xmlunit.XMLAssert.assertXpathEvaluatesTo; import static org.custommonkey.xmlunit.XMLAssert.assertXpathExists; import static org.custommonkey.xmlunit.XMLAssert.assertXpathNotExists; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static junit.framework.Assert.assertEquals; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ForkJoinPool; import org.codice.ddf.parser.Parser; import org.codice.ddf.parser.xml.XmlParser; import org.custommonkey.xmlunit.NamespaceContext; import org.custommonkey.xmlunit.SimpleNamespaceContext; import org.custommonkey.xmlunit.XMLUnit; import org.custommonkey.xmlunit.exceptions.XpathException; import org.joda.time.DateTime; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; import ddf.catalog.data.AttributeDescriptor; import ddf.catalog.data.BinaryContent; import ddf.catalog.data.Metacard; import ddf.catalog.data.MetacardType; import ddf.catalog.data.Result; import ddf.catalog.data.impl.BasicTypes; import ddf.catalog.data.impl.MetacardImpl; import ddf.catalog.data.impl.ResultImpl; import ddf.catalog.operation.SourceResponse; import ddf.catalog.operation.impl.SourceResponseImpl; import ddf.catalog.transform.CatalogTransformerException; import ddf.catalog.transformer.xml.XmlResponseQueueTransformer; /** * Tests the {@link XmlResponseQueueTransformer} transformations */ public class TestXmlResponseQueueTransformer { private static final String DEFAULT_ID = "myID"; private boolean verboseDebug = false; private static final String DEFAULT_TYPE_NAME = BasicTypes.BASIC_METACARD.getName(); private static final Date DEFAULT_EXPIRATION_DATE = new DateTime(123456789).toDate(); private static final String DEFAULT_TITLE = "myTitle"; private static final String DEFAULT_GEO = "POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10),(20 30, 35 35, 30 20, 20 30))"; private static final String DEFAULT_METADATA = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><foo><bar/></foo>"; private static final byte[] DEFAULT_BYTES = new byte[] {0, 0, 1}; private static final String DEFAULT_BASE64 = "AAAB"; private static final Logger LOGGER = LoggerFactory .getLogger(TestXmlResponseQueueTransformer.class); private static final String DEFAULT_SOURCE_ID = "mySourceId"; private static final ForkJoinPool FJP = new ForkJoinPool(); private XmlResponseQueueTransformer transformer; private Parser parser; @BeforeClass public static void setupTestClass() { // makes xpaths easier to write when prefixes are declared beforehand. HashMap<String, String> map = new HashMap<>(); map.put("gml", "http://www.opengis.net/gml"); map.put("mc", "urn:catalog:metacard"); NamespaceContext ctx = new SimpleNamespaceContext(map); XMLUnit.setXpathNamespaceContext(ctx); } @Before public void setup() { parser = new XmlParser(); transformer = new XmlResponseQueueTransformer(parser, FJP); } /** * Should throw exception when given {@code null} input * * @throws CatalogTransformerException */ @Test(expected = CatalogTransformerException.class) public void testNullSourceResponse() throws CatalogTransformerException { // given transformer.setThreshold(2); // when BinaryContent binaryContent = transformer.transform(null, null); // then // failure should occur } /** * No {@link MetacardType} name should use the default name. * * @throws CatalogTransformerException * @throws IOException * @throws SAXException * @throws XpathException */ @Test public void testMetacardTypeNameNull() throws CatalogTransformerException, IOException, XpathException, SAXException { // given transformer.setThreshold(2); SourceResponse response = givenMetacardTypeName(null); // when BinaryContent binaryContent = transformer.transform(response, null); // then assertThat(binaryContent.getMimeType(), is(XmlResponseQueueTransformer.MIME_TYPE)); byte[] bytes = binaryContent.getByteArray(); String output = new String(bytes); print(output, verboseDebug); assertXpathEvaluatesTo(DEFAULT_TYPE_NAME, "/mc:metacards/mc:metacard/mc:type", output); } /** * No {@link MetacardType} name should use the default name. * * @throws CatalogTransformerException * @throws IOException * @throws SAXException * @throws XpathException */ @Test public void testMetacardTypeNameEmpty() throws CatalogTransformerException, IOException, XpathException, SAXException { // given transformer.setThreshold(2); SourceResponse response = givenMetacardTypeName(""); // when BinaryContent binaryContent = transformer.transform(response, null); // then assertThat(binaryContent.getMimeType(), is(XmlResponseQueueTransformer.MIME_TYPE)); byte[] bytes = binaryContent.getByteArray(); String output = new String(bytes); print(output, verboseDebug); assertXpathEvaluatesTo(DEFAULT_TYPE_NAME, "/mc:metacards/mc:metacard/mc:type", output); } @Test public void testNoIdNoSourceId() throws CatalogTransformerException, IOException, XpathException, SAXException { // given transformer.setThreshold(2); SourceResponse response = givenSourceResponse(null, null); // when BinaryContent binaryContent = transformer.transform(response, null); // then assertThat(binaryContent.getMimeType(), is(XmlResponseQueueTransformer.MIME_TYPE)); byte[] bytes = binaryContent.getByteArray(); String output = new String(bytes); print(output, verboseDebug); assertXpathNotExists("/mc:metacards/mc:metacard/mc:source", output); assertXpathNotExists("/mc:metacards/mc:metacard/@gml:id", output); verifyDefaults("1", output); } @Test public void testNoId() throws CatalogTransformerException, IOException, XpathException, SAXException { // given transformer.setThreshold(2); SourceResponse response = givenSourceResponse(DEFAULT_SOURCE_ID, null); // when BinaryContent binaryContent = transformer.transform(response, null); // then assertThat(binaryContent.getMimeType(), is(XmlResponseQueueTransformer.MIME_TYPE)); byte[] bytes = binaryContent.getByteArray(); String output = new String(bytes); print(output, verboseDebug); assertXpathEvaluatesTo(DEFAULT_SOURCE_ID, "/mc:metacards/mc:metacard/mc:source", output); assertXpathNotExists("/mc:metacards/mc:metacard/@gml:id", output); verifyDefaults("1", output); } @Test public void testStub() throws CatalogTransformerException, IOException, XpathException, SAXException { // given transformer.setThreshold(2); SourceResponse response = givenSourceResponse(DEFAULT_SOURCE_ID, DEFAULT_ID); // when BinaryContent binaryContent = transformer.transform(response, null); // then assertThat(binaryContent.getMimeType(), is(XmlResponseQueueTransformer.MIME_TYPE)); byte[] bytes = binaryContent.getByteArray(); String output = new String(bytes); print(output, verboseDebug); assertXpathEvaluatesTo(DEFAULT_SOURCE_ID, "/mc:metacards/mc:metacard/mc:source", output); assertXpathEvaluatesTo(DEFAULT_ID, "/mc:metacards/mc:metacard/@gml:id", output); verifyDefaults("1", output); } @Test public void testMultiple() throws CatalogTransformerException, IOException, XpathException, SAXException { // given transformer.setThreshold(2); SourceResponse response = givenSourceResponse(new MetacardStub("source1", "id1"), new MetacardStub("source2", "id2"), new MetacardStub("source3", "id3")); // when BinaryContent binaryContent = transformer.transform(response, null); // then assertThat(binaryContent.getMimeType(), is(XmlResponseQueueTransformer.MIME_TYPE)); byte[] bytes = binaryContent.getByteArray(); String output = new String(bytes); print(output, verboseDebug); assertXpathEvaluatesTo("source1", "/mc:metacards/mc:metacard[1]/mc:source", output); assertXpathEvaluatesTo("id1", "/mc:metacards/mc:metacard[1]/@gml:id", output); assertXpathEvaluatesTo("source2", "/mc:metacards/mc:metacard[2]/mc:source", output); assertXpathEvaluatesTo("id2", "/mc:metacards/mc:metacard[2]/@gml:id", output); assertXpathEvaluatesTo("source3", "/mc:metacards/mc:metacard[3]/mc:source", output); assertXpathEvaluatesTo("id3", "/mc:metacards/mc:metacard[3]/@gml:id", output); assertXpathEvaluatesTo("3", "count(/mc:metacards/mc:metacard)", output); verifyDefaults("1", output); verifyDefaults("2", output); verifyDefaults("3", output); } @Test public void testCompareSerialToFork() throws IOException, CatalogTransformerException { SourceResponse response = givenSourceResponse(new MetacardStub("source1", "id1"), new MetacardStub("source2", "id2"), new MetacardStub("source3", "id3"), new MetacardStub("source4", "id4")); XmlResponseQueueTransformer serialXform = new XmlResponseQueueTransformer(parser, FJP); serialXform.setThreshold(2); XmlResponseQueueTransformer forkXForm = new XmlResponseQueueTransformer(parser, FJP); forkXForm.setThreshold(10); BinaryContent serialBc = serialXform.transform(response, null); BinaryContent forkBc = forkXForm.transform(response, null); String serialOutput = new String(serialBc.getByteArray()); String forkOutput = new String(forkBc.getByteArray()); // There are expected whitespace differences between the outputs. // This is an overly aggressive conversion; a better test would be to unmarshal the // xml metacards back into Metacard instances and compare equality. assertEquals(serialOutput.replaceAll("\\s", ""), forkOutput.replaceAll("\\s", "")); } @Test public void testXmlResponseQueueTransformer() throws Exception { MetacardImpl mc = new MetacardImpl(); final String testId = "1234567890987654321"; final String testSource = "FooBarSource"; final String testTitle = "Title!"; final Date testDate = new Date(); final String testLocation = "POLYGON ((35 10, 10 20, 15 40, 45 45, 35 10),(20 30, 35 35, 30 20, 20 30))"; final byte[] testThumbnail = {0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1}; mc.setId(testId); mc.setSourceId(testSource); mc.setTitle(testTitle); mc.setExpirationDate(testDate); mc.setLocation(testLocation); mc.setThumbnail(testThumbnail); String metadata; try (FileInputStream stream = new FileInputStream( new File("src/test/resources/extensibleMetacard.xml"))) { FileChannel fc = stream.getChannel(); MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); /* Instead of using default, pass in a decoder. */ metadata = Charset.defaultCharset().decode(bb).toString(); } mc.setMetadata(metadata); Metacard mci = mc; transformer.setThreshold(2); SourceResponse response = new SourceResponseImpl(null, Arrays.asList((Result) new ResultImpl(mci))); BinaryContent bc = transformer.transform(response, null); if (bc == null) { fail("Binary Content is null."); } String outputXml = new String(bc.getByteArray()); LOGGER.debug(outputXml); assertXpathEvaluatesTo(testId, "/mc:metacards/mc:metacard/@gml:id", outputXml); assertXpathEvaluatesTo(testSource, "/mc:metacards/mc:metacard/mc:source", outputXml); assertXpathEvaluatesTo(testTitle, "/mc:metacards/mc:metacard/mc:string[@name='title']/mc:value", outputXml); // TODO convert GML representation? // outputXml); assertXpathExists("/mc:metacards/mc:metacard/mc:geometry[@name='location']/mc:value", outputXml); assertXpathExists("/mc:metacards/mc:metacard/mc:base64Binary[@name='thumbnail']/mc:value", outputXml); // TODO XML Date representation? assertXpathExists("/mc:metacards/mc:metacard/mc:dateTime[@name='expiration']/mc:value", outputXml); } /** * @return */ private MetacardType getMetacardTypeStub(String name, Set<AttributeDescriptor> descriptors) { MetacardType type = mock(MetacardType.class); when(type.getName()).thenReturn(name); when(type.getAttributeDescriptors()).thenReturn(descriptors); return type; } /** * @param index TODO * @param output * @throws IOException * @throws SAXException * @throws XpathException */ private void verifyDefaults(String index, String output) throws IOException, SAXException, XpathException { assertXpathEvaluatesTo(DEFAULT_TYPE_NAME, "/mc:metacards/mc:metacard[" + index + "]/mc:type", output); assertXpathExists("/mc:metacards/mc:metacard[" + index + "]/mc:geometry[@name='location']//gml:Polygon", output); assertXpathExists( "/mc:metacards/mc:metacard[" + index + "]/mc:dateTime[@name='expiration']", output); assertXpathExists("/mc:metacards/mc:metacard[" + index + "]/mc:stringxml[@name='metadata']", output); assertXpathEvaluatesTo(DEFAULT_TITLE, "/mc:metacards/mc:metacard[" + index + "]/mc:string[@name='title']/mc:value", output); assertXpathEvaluatesTo(DEFAULT_BASE64, "/mc:metacards/mc:metacard[" + index + "]/mc:base64Binary[@name='thumbnail']/mc:value", output); } /** * @return */ private SourceResponseImpl givenSourceResponse(String sourceId, String id) { return new SourceResponseImpl(null, Arrays.asList((Result) new ResultImpl(new MetacardStub(sourceId, id)))); } private SourceResponseImpl givenSourceResponse(Metacard... metacards) { List<Result> results = new ArrayList<Result>(); for (Metacard m : metacards) { results.add(new ResultImpl(m)); } return new SourceResponseImpl(null, results); } private SourceResponse givenMetacardTypeName(String metacardTypeName) { MetacardType type = getMetacardTypeStub(metacardTypeName, new HashSet<AttributeDescriptor>()); Metacard metacard = new MetacardImpl(type); SourceResponse response = new SourceResponseImpl(null, Arrays.asList((Result) new ResultImpl(metacard))); return response; } private void print(String output, boolean inFull) { if (inFull) { LOGGER.debug(output); } } class MetacardStub extends MetacardImpl { public MetacardStub(String sourceId, String id) { super(new MetacardImpl()); setSourceId(sourceId); setId(id); setTitle(DEFAULT_TITLE); setExpirationDate(DEFAULT_EXPIRATION_DATE); setLocation(DEFAULT_GEO); setMetadata(DEFAULT_METADATA); setThumbnail(DEFAULT_BYTES); } } }