package org.milyn.delivery;
import static org.milyn.delivery.SAXHandlerTest.SAXMatchers.isSaxElementWithQName;
import static org.milyn.delivery.SAXHandlerTest.SAXMatchers.isSaxFragmentWithQName;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import javax.xml.namespace.QName;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamSource;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Test;
import org.milyn.Smooks;
import org.milyn.SmooksException;
import org.milyn.container.ExecutionContext;
import org.milyn.delivery.sax.SAXElement;
import org.milyn.delivery.sax.SAXHandler;
import org.milyn.delivery.sax.SAXVisitBefore;
/**
* Test for {@link SAXHandler}.
*
* @author Michael Krüske
*/
public class SAXHandlerTest {
private static final String SIMPLE_SAMPLE_XML = "SAXHandlerTest.xml";
private static final String URN_SIMPLE = "urn:simple";
private static final String URN_FIRST = "urn:first";
private static final String URN_SECOND = "urn:second";
private static final QName QNAME_FOR_FIRST_SAMPLE = new QName(URN_FIRST, "sample");
private static final QName QNAME_FOR_SECOND_SAMPLE = new QName(URN_SECOND, "sample");
/**
* Parse a simple XML file containing two tags only distinguished by their namespaces.
*
* This test checks that the {@see VisitLifecycleCleanable#executeVisitLifecycleCleanup(Fragment, ExecutionContext)} are only
* called for elements matching the full qualified tag name, including the namespace.
* Additionally it will also ensure this for {@see SAXVisitBefore#visitBefore(SAXElement, ExecutionContext)}.
*
* Test for MILYN-648 Only execute clean-up handlers targeted at element.
*
* @throws SmooksException if parsing fails
* @throws IOException if reading fails
*/
@Test
public void executeLifeCycleCleanup_onlyForTargetElements() throws SmooksException, IOException {
// given
final VisitBeforeAndLifecycleCleanable firstMock = mock(VisitBeforeAndLifecycleCleanable.class);
final VisitBeforeAndLifecycleCleanable secondMock = mock(VisitBeforeAndLifecycleCleanable.class);
final Smooks smooks = createSmooks();
smooks.addVisitor(firstMock, "simple:simple/first:sample");
smooks.addVisitor(secondMock, "simple:simple/second:sample");
// when
smooks.filterSource(smooks.createExecutionContext(), createSource(), mock(Result.class));
// then
verify(firstMock).visitBefore(
argThat(isSaxElementWithQName(QNAME_FOR_FIRST_SAMPLE)),
any(ExecutionContext.class));
verify(firstMock, never()).visitBefore(
argThat(isSaxElementWithQName(QNAME_FOR_SECOND_SAMPLE)),
any(ExecutionContext.class));
verify(firstMock).executeVisitLifecycleCleanup(
argThat(isSaxFragmentWithQName(QNAME_FOR_FIRST_SAMPLE)),
any(ExecutionContext.class));
verify(firstMock, never()).executeVisitLifecycleCleanup(
argThat(isSaxFragmentWithQName(QNAME_FOR_SECOND_SAMPLE)),
any(ExecutionContext.class));
verify(secondMock).visitBefore(
argThat(isSaxElementWithQName(QNAME_FOR_SECOND_SAMPLE)),
any(ExecutionContext.class));
verify(secondMock, never()).visitBefore(
argThat(isSaxElementWithQName(QNAME_FOR_FIRST_SAMPLE)),
any(ExecutionContext.class));
verify(secondMock).executeVisitLifecycleCleanup(
argThat(isSaxFragmentWithQName(QNAME_FOR_SECOND_SAMPLE)),
any(ExecutionContext.class));
verify(secondMock, never()).executeVisitLifecycleCleanup(
argThat(isSaxFragmentWithQName(QNAME_FOR_FIRST_SAMPLE)),
any(ExecutionContext.class));
verifyNoMoreInteractions(firstMock, secondMock);
}
private StreamSource createSource() {
final InputStream inputStream = SAXHandlerTest.class
.getResourceAsStream(SIMPLE_SAMPLE_XML);
StreamSource source = new StreamSource(inputStream);
return source;
}
private Smooks createSmooks() {
final Smooks smooks = new Smooks();
smooks.setNamespaces(createNamespacesMap());
return smooks;
}
private Properties createNamespacesMap() {
Properties namespaces = new Properties();
namespaces.put("simple", URN_SIMPLE);
namespaces.put("first", URN_FIRST);
namespaces.put("second", URN_SECOND);
return namespaces;
}
private interface VisitBeforeAndLifecycleCleanable extends SAXVisitBefore, VisitLifecycleCleanable {
}
static class SAXMatchers {
private SAXMatchers() {
}
@Factory
public static <T> Matcher<SAXElement> isSaxElementWithQName(final QName qname) {
return new IsSaxElement(qname);
}
@Factory
public static <T> Matcher<Fragment> isSaxFragmentWithQName(final QName qname) {
return new IsSaxFragment(qname);
}
}
private static class IsSaxElement extends TypeSafeMatcher<SAXElement> {
private final QName qname;
public IsSaxElement(final QName qname) {
if (qname == null) throw new IllegalArgumentException("qname must not be null.");
this.qname = qname;
}
@Override
public boolean matchesSafely(SAXElement element) {
return qname.equals(element.getName());
}
public void describeTo(Description description) {
description.appendText("is a SAXElement with the qualified name " + qname);
}
}
private static class IsSaxFragment extends TypeSafeMatcher<Fragment> {
private final QName qname;
public IsSaxFragment(final QName qname) {
if (qname == null) throw new IllegalArgumentException("qname must not be null.");
this.qname = qname;
}
@Override
public boolean matchesSafely(Fragment element) {
return qname.equals(element.getSAXElement().getName());
}
public void describeTo(Description description) {
description.appendText("is a SAX fragment with the qualified name " + qname);
}
}
}