/*
* Copyright (C) 2008 Laurent Caillette
*
* This program 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 (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.novelang.outfit.xml;
import java.io.IOException;
import java.io.StringReader;
import java.util.Iterator;
import com.google.common.collect.ImmutableList;
import org.junit.Test;
import org.mockito.Matchers;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;
import static org.fest.assertions.Assertions.assertThat;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;
/**
* Tests for {@link SaxRecorder}.
*
* @author Laurent Caillette
*/
public class SaxRecorderTest {
@Test
public void parseOk() throws SAXException, IOException {
final SaxRecorder recorder = new SaxRecorder() ;
final XMLReader xmlReader = XMLReaderFactory.createXMLReader() ;
xmlReader.setContentHandler( recorder ) ;
xmlReader.parse( new InputSource( new StringReader( XML ) ) ) ;
// Using a spy to use implementation of get/setLocator.
final ContentHandlerAdapter target = spy( new ContentHandlerAdapter() ) ;
final LocationGrabber locationGrabber = installLocationGrabber( target ) ;
final SaxRecorder.Player player = recorder.getPlayer() ;
player.playOn( target ) ;
final Iterator< ImmutableSourceLocator > locations = locationGrabber.getLocationsIterator() ;
verify( target ).startDocument() ;
assertThat( locations.next() ).isEqualTo( new ImmutableSourceLocator( 1, 1 ) ) ;
verify( target ).startElement( "", "root", "root", new ImmutableAttributes() ) ;
assertThat( locations.next() ).isEqualTo( new ImmutableSourceLocator( 2, 7 ) ) ;
/*
final ArgumentCaptor< Object > argument0 = ArgumentCaptor.forClass( Object.class ) ;
final ArgumentCaptor< Integer > argument1 = ArgumentCaptor.forClass( Integer.class ) ;
final ArgumentCaptor< Integer > argument2 = ArgumentCaptor.forClass( Integer.class ) ;
verify( target ).characters(
( char[] ) argument0.capture(), argument1.capture(), argument2.capture() ) ;
final String text = new String(
( char[] ) argument0.getValue(), argument1.getValue(), argument2.getValue() ) ;
*/
/*
final ArgumentCaptor< Object > argument = ArgumentCaptor.forClass( Object.class ) ;
verify( target ).characters(
( char[] ) argument.capture(),
( Integer ) argument.capture(),
( Integer ) argument.capture()
) ;
final List< Object > arguments = argument.getAllValues() ;
final String text = new String(
( char[] ) arguments.get( 0 ),
( Integer ) arguments.get( 1 ),
( Integer ) arguments.get( 2 )
) ;
assertThat( text ).isEqualTo( "text" ) ;
*/
// Too bad, Mockito doesn't seem to handle multiple captures for the same method call.
verify( target, atLeastOnce() ).characters(
Matchers.< char[] >anyObject(), anyInt(), anyInt() ) ;
verify( target ).startElement(
"", "child", "child",
new ImmutableAttributes.Builder().add( "", "a", "a", "CDATA", "value" ).build()
) ;
assertThat( locations.next() ).isEqualTo( new ImmutableSourceLocator( 4, 21 ) ) ;
verify( target ).endElement( "", "child", "child" ) ;
assertThat( locations.next() ).isEqualTo( new ImmutableSourceLocator( 4, 21 ) ) ;
verify( target ).endElement( "", "root", "root" ) ;
assertThat( locations.next() ).isEqualTo( new ImmutableSourceLocator( 5, 8 ) ) ;
verify( target ).endDocument() ;
assertThat( locations.next() ).isEqualTo( new ImmutableSourceLocator( 5, 8 ) ) ;
assertThat( locations.hasNext() ).isFalse() ;
assertThat( SaxRecorder.asXml( player ) ).isEqualTo( XML ) ;
}
// =======
// Fixture
// =======
private static final String BREAK = "\n" ;
private static final String XML =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + BREAK // 1
+ "<root>" + BREAK // 2
+ " text " + BREAK // 3
+ " <child a=\"value\"/>" + BREAK // 4
+ "</root>" // 5
;
private static LocationGrabber installLocationGrabber( final ContentHandlerAdapter target )
throws SAXException
{
final LocationGrabber locationGrabber = new LocationGrabber() ;
doAnswer( locationGrabber ).when( target ).startDocument() ;
doAnswer( locationGrabber ).when( target ).startElement(
anyString(), anyString(), anyString(), anyAttributes() ) ;
doAnswer( locationGrabber ).when( target ).endElement( anyString(), anyString(), anyString() ) ;
doAnswer( locationGrabber ).when( target ).endDocument() ;
return locationGrabber;
}
/**
* Performs a side-effect for storing current location in the {@code ContentHandler}
* upon call of stubbed method.
*/
private static class LocationGrabber implements Answer {
private final ImmutableList.Builder< ImmutableSourceLocator > locations =
ImmutableList.builder() ;
@Override
public Object answer( final InvocationOnMock invocation ) throws Throwable {
final ContentHandlerAdapter locatorOwner = ( ContentHandlerAdapter ) invocation.getMock() ;
locations.add( ImmutableSourceLocator.create( locatorOwner.getDocumentLocator() ) ) ;
return null ;
}
public ImmutableList<ImmutableSourceLocator> getLocations() {
return locations.build() ;
}
public Iterator<ImmutableSourceLocator> getLocationsIterator() {
return getLocations().iterator() ;
}
}
/**
* Type sugar.
*/
private static Attributes anyAttributes() {
return Matchers.any() ;
}
}