package org.realityforge.gwt.appcache.server.mgwt; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.realityforge.gwt.appcache.server.TestPropertyProvider; import org.realityforge.gwt.appcache.server.propertyprovider.PropertyProvider; import org.testng.annotations.Test; import static org.mockito.Mockito.*; public class AbstractMgwtManifestServletTest { static class TestManifestServlet extends AbstractMgwtManifestServlet { private ServletContext _servletContext; @Override public ServletContext getServletContext() { if ( null == _servletContext ) { _servletContext = mock( ServletContext.class ); } return _servletContext; } protected final void addPropertyProviderForTest( final PropertyProvider propertyProvider ) { addPropertyProvider( propertyProvider ); } protected final void addClientSideSelectionPropertyForTest( final String propertyName ) { addClientSideSelectionProperty( propertyName ); } } @Test public void serve_withExactMatch() throws Exception { final TestManifestServlet servlet = new TestManifestServlet(); final String strongPermutation = "C7D408F8EFA266A7F9A31209F8AA7446"; final String userAgent = "safari"; final String mobileUserAgent = "mobilesafari"; final String os = MgwtOsPropertyProvider.iPhone.getValue(); setProperties( servlet, userAgent, mobileUserAgent, os ); setupPermutationsXML( servlet, strongPermutation, userAgent, mobileUserAgent, os ); final String manifestContent = setupManifestFile( servlet, strongPermutation ); final HttpServletResponse response = mock( HttpServletResponse.class ); final ServletOutputStream output = mock( ServletOutputStream.class ); when( response.getOutputStream() ).thenReturn( output ); performRequest( servlet, response ); verify( output ).write( manifestContent.getBytes( "US-ASCII" ) ); } @Test public void serve_withMergeOfRetinaAndNonRetina() throws Exception { final TestManifestServlet servlet = new TestManifestServlet(); final String permutation1 = "A"; final String permutation2 = "B"; final String userAgent = "safari"; final String mobileUserAgent = "mobilesafari"; setProperties( servlet, userAgent, mobileUserAgent, MgwtOsPropertyProvider.iPhone_undefined.getValue() ); setupPermutationsXML( servlet, permutation1, userAgent, mobileUserAgent, MgwtOsPropertyProvider.iPhone.getValue(), permutation2, MgwtOsPropertyProvider.retina.getValue() ); setupManifestFile( servlet, permutation1 ); setupManifestFile( servlet, permutation2 ); final HttpServletResponse response = mock( HttpServletResponse.class ); final ServletOutputStream output = mock( ServletOutputStream.class ); when( response.getOutputStream() ).thenReturn( output ); performRequest( servlet, response ); final String combinedManifest = "CACHE MANIFEST\n" + "\n" + "CACHE:\n" + permutation1 + ".txt\n" + permutation2 + ".txt\n"; verify( output ).write( combinedManifest.getBytes( "US-ASCII" ) ); } @Test public void serve_withMergeOfRetinaAndNonRetinaAndSoftLanguagePermutation() throws Exception { final TestManifestServlet servlet = new TestManifestServlet(); //iphone english final String permutation1 = "A"; //iphone retina english final String permutation2 = "B"; //iphone german final String permutation3 = "C"; //iphone retina german final String permutation4 = "D"; final String userAgent = "safari"; final String mobileUserAgent = "mobilesafari"; setProperties( servlet, userAgent, mobileUserAgent, MgwtOsPropertyProvider.iPhone_undefined.getValue() ); final String iPhone = MgwtOsPropertyProvider.iPhone.getValue(); final String retina = MgwtOsPropertyProvider.retina.getValue(); servlet.addClientSideSelectionPropertyForTest( "locale" ); final String permutationContent = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<permutations>\n" + singlePermutationBlock( permutation1, userAgent, mobileUserAgent, iPhone, "en" ) + singlePermutationBlock( permutation2, userAgent, mobileUserAgent, retina, "en" ) + singlePermutationBlock( permutation3, userAgent, mobileUserAgent, iPhone, "de" ) + singlePermutationBlock( permutation4, userAgent, mobileUserAgent, retina, "de" ) + "</permutations>\n"; createPermutationsXML( servlet, permutationContent ); setupManifestFile( servlet, permutation1 ); setupManifestFile( servlet, permutation2 ); setupManifestFile( servlet, permutation3 ); setupManifestFile( servlet, permutation4 ); final HttpServletResponse response = mock( HttpServletResponse.class ); final ServletOutputStream output = mock( ServletOutputStream.class ); when( response.getOutputStream() ).thenReturn( output ); performRequest( servlet, response ); final String combinedManifest = "CACHE MANIFEST\n" + "\n" + "CACHE:\n" + permutation1 + ".txt\n" + permutation2 + ".txt\n" + permutation3 + ".txt\n" + permutation4 + ".txt\n"; verify( output ).write( combinedManifest.getBytes( "US-ASCII" ) ); } @Test public void serve_withMergeOfRetinaAndNonRetinaWithLanguageSelected() throws Exception { final TestManifestServlet servlet = new TestManifestServlet(); //iphone english final String permutation1 = "A"; //iphone retina english final String permutation2 = "B"; //iphone german final String permutation3 = "C"; //iphone retina german final String permutation4 = "D"; final String userAgent = "safari"; final String mobileUserAgent = "mobilesafari"; setProperties( servlet, userAgent, mobileUserAgent, MgwtOsPropertyProvider.iPhone_undefined.getValue() ); servlet.addPropertyProviderForTest( new TestPropertyProvider( "locale", "en" ) ); servlet.addClientSideSelectionPropertyForTest( "locale" ); final String iPhone = MgwtOsPropertyProvider.iPhone.getValue(); final String retina = MgwtOsPropertyProvider.retina.getValue(); final String permutationContent = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<permutations>\n" + singlePermutationBlock( permutation1, userAgent, mobileUserAgent, iPhone, "en" ) + singlePermutationBlock( permutation2, userAgent, mobileUserAgent, retina, "en" ) + singlePermutationBlock( permutation3, userAgent, mobileUserAgent, iPhone, "de" ) + singlePermutationBlock( permutation4, userAgent, mobileUserAgent, retina, "de" ) + "</permutations>\n"; createPermutationsXML( servlet, permutationContent ); setupManifestFile( servlet, permutation1 ); setupManifestFile( servlet, permutation2 ); setupManifestFile( servlet, permutation3 ); setupManifestFile( servlet, permutation4 ); final HttpServletResponse response = mock( HttpServletResponse.class ); final ServletOutputStream output = mock( ServletOutputStream.class ); when( response.getOutputStream() ).thenReturn( output ); performRequest( servlet, response ); final String combinedManifest = "CACHE MANIFEST\n" + "\n" + "CACHE:\n" + permutation1 + ".txt\n" + permutation2 + ".txt\n"; verify( output ).write( combinedManifest.getBytes( "US-ASCII" ) ); } @Test public void serve_withMissingRetina() throws Exception { final TestManifestServlet servlet = new TestManifestServlet(); final String permutation1 = "A"; final String userAgent = "safari"; final String mobileUserAgent = "mobilesafari"; setProperties( servlet, userAgent, mobileUserAgent, MgwtOsPropertyProvider.iPhone_undefined.getValue() ); setupPermutationsXML( servlet, permutation1, userAgent, mobileUserAgent, MgwtOsPropertyProvider.iPhone.getValue() ); setupManifestFile( servlet, permutation1 ); final HttpServletResponse response = mock( HttpServletResponse.class ); final ServletOutputStream output = mock( ServletOutputStream.class ); when( response.getOutputStream() ).thenReturn( output ); performRequest( servlet, response ); verify( response ).sendError( HttpServletResponse.SC_NOT_FOUND ); } @Test public void serve_withMissingNonRetina() throws Exception { final TestManifestServlet servlet = new TestManifestServlet(); final String permutation1 = "A"; final String userAgent = "safari"; final String mobileUserAgent = "mobilesafari"; setProperties( servlet, userAgent, mobileUserAgent, MgwtOsPropertyProvider.iPhone_undefined.getValue() ); setupPermutationsXML( servlet, permutation1, userAgent, mobileUserAgent, MgwtOsPropertyProvider.retina.getValue() ); setupManifestFile( servlet, permutation1 ); final HttpServletResponse response = mock( HttpServletResponse.class ); performRequest( servlet, response ); verify( response ).sendError( HttpServletResponse.SC_NOT_FOUND ); } @Test public void serve_withNoMatch() throws Exception { final TestManifestServlet servlet = new TestManifestServlet(); setProperties( servlet, "safari", "mobilesafari", MgwtOsPropertyProvider.retina.getValue() ); setupPermutationsXML( servlet, "X", "X", "X", "X" ); setupManifestFile( servlet, "A" ); final HttpServletResponse response = mock( HttpServletResponse.class ); performRequest( servlet, response ); verify( response ).sendError( HttpServletResponse.SC_NOT_FOUND ); } private void performRequest( final TestManifestServlet servlet, final HttpServletResponse response ) throws ServletException, IOException { final HttpServletRequest request = mock( HttpServletRequest.class ); when( request.getServletPath() ).thenReturn( "/mymodule.appcache" ); when( request.getMethod() ).thenReturn( "GET" ); servlet.service( request, response ); } private String setupManifestFile( final TestManifestServlet servlet, final String strongPermutation ) throws IOException { final String manifestContent = "CACHE MANIFEST\n" + strongPermutation + ".txt\n"; final File manifest = createFile( "manifest", "appcache", manifestContent ); when( servlet.getServletContext().getResourceAsStream( "/mymodule/" + strongPermutation + ".appcache" ) ). thenReturn( new FileInputStream( manifest ) ); return manifestContent; } private void setupPermutationsXML( final TestManifestServlet servlet, final String strongPermutation, final String userAgent, final String mobileUserAgent, final String os ) throws IOException { final String permutationContent = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<permutations>\n" + " <permutation name=\"" + strongPermutation + "\">\n" + " <user.agent>" + userAgent + "</user.agent>\n" + " <mobile.user.agent>" + mobileUserAgent + "</mobile.user.agent>\n" + " <mgwt.os>" + os + "</mgwt.os>\n" + " </permutation>\n" + "</permutations>\n"; createPermutationsXML( servlet, permutationContent ); } private void setupPermutationsXML( final TestManifestServlet servlet, final String permutation1, final String userAgent, final String mobileUserAgent, final String os1, final String permutation2, final String os2 ) throws IOException { final String permutationContent = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<permutations>\n" + singlePermutationBlock( permutation1, userAgent, mobileUserAgent, os1, null ) + singlePermutationBlock( permutation2, userAgent, mobileUserAgent, os2, null ) + "</permutations>\n"; createPermutationsXML( servlet, permutationContent ); } private String singlePermutationBlock( @Nonnull final String name, @Nonnull final String userAgent, @Nonnull final String mobileUserAgent, @Nonnull final String os, @Nullable final String language ) { final StringBuilder sb = new StringBuilder(); sb.append( " <permutation name=\"" + name + "\">\n" ); sb.append( " <user.agent>" ); sb.append( userAgent ); sb.append( "</user.agent>\n" ); sb.append( " <mobile.user.agent>" ); sb.append( mobileUserAgent ); sb.append( "</mobile.user.agent>\n" ); sb.append( " <mgwt.os>" ); sb.append( os ); sb.append( "</mgwt.os>\n" ); if ( null != language ) { sb.append( " <locale>" ); sb.append( language ); sb.append( "</locale>\n" ); } sb.append( " </permutation>\n" ); return sb.toString(); } private void createPermutationsXML( final TestManifestServlet servlet, final String fileContent ) throws IOException { final File file = createFile( "permutations", "xml", fileContent ); when( servlet.getServletContext().getResource( "/mymodule/permutations.xml" ) ). thenReturn( file.toURI().toURL() ); when( servlet.getServletContext().getResourceAsStream( "/mymodule/permutations.xml" ) ). thenReturn( new FileInputStream( file ) ); } private void setProperties( final TestManifestServlet servlet, final String userAgent, final String mobileUserAgent, final String os ) { servlet.addPropertyProviderForTest( new TestPropertyProvider( "user.agent", userAgent ) ); servlet.addPropertyProviderForTest( new TestPropertyProvider( "mobile.user.agent", mobileUserAgent ) ); servlet.addPropertyProviderForTest( new TestPropertyProvider( "mgwt.os", os ) ); } private File createFile( final String prefix, final String extension, final String content ) throws IOException { final File file = File.createTempFile( prefix, extension ); file.deleteOnExit(); final FileOutputStream outputStream = new FileOutputStream( file ); outputStream.write( content.getBytes() ); outputStream.close(); return file; } }