mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2025-08-02 11:05:29 -04:00
Added test case for ImageFilter + implemented changes suggested by Rune Bremnes.
This commit is contained in:
parent
b5b1b4f422
commit
521c4e4bbc
@ -115,23 +115,25 @@ public abstract class ImageFilter extends GenericFilter {
|
||||
imageResponse.setImage(image);
|
||||
//System.out.println("Done.");
|
||||
|
||||
if (encode) {
|
||||
//System.out.println("Encoding image...");
|
||||
// Encode image to original response
|
||||
if (image != null) {
|
||||
// TODO: Be smarter than this...
|
||||
// TODO: Make sure ETag is same, if image content is the same...
|
||||
// Use ETag of original response (or derived from)
|
||||
// Use last modified of original response? Or keep original resource's, don't set at all?
|
||||
// TODO: Why weak ETag?
|
||||
String etag = "W/\"" + Integer.toHexString(hashCode()) + "-" + Integer.toHexString(image.hashCode()) + "\"";
|
||||
// TODO: This breaks for wrapped instances, need to either unwrap or test for HttpSR...
|
||||
((HttpServletResponse) pResponse).setHeader("ETag", etag);
|
||||
((HttpServletResponse) pResponse).setDateHeader("Last-Modified", (System.currentTimeMillis() / 1000) * 1000);
|
||||
imageResponse.flush();
|
||||
}
|
||||
//System.out.println("Done encoding.");
|
||||
}
|
||||
|
||||
if (encode) {
|
||||
//System.out.println("Encoding image...");
|
||||
// Encode image to original response
|
||||
if (image != null) {
|
||||
// TODO: Be smarter than this...
|
||||
// TODO: Make sure ETag is same, if image content is the same...
|
||||
// Use ETag of original response (or derived from)
|
||||
// Use last modified of original response? Or keep original resource's, don't set at all?
|
||||
// TODO: Why weak ETag?
|
||||
String etag = "W/\"" + Integer.toHexString(hashCode()) + "-" + Integer.toHexString(image.hashCode()) + "\"";
|
||||
// TODO: This breaks for wrapped instances, need to either unwrap or test for HttpSR...
|
||||
((HttpServletResponse) pResponse).setHeader("ETag", etag);
|
||||
((HttpServletResponse) pResponse).setDateHeader("Last-Modified", (System.currentTimeMillis() / 1000) * 1000);
|
||||
}
|
||||
|
||||
imageResponse.flush();
|
||||
//System.out.println("Done encoding.");
|
||||
}
|
||||
}
|
||||
//System.out.println("Filtering done.");
|
||||
|
@ -242,9 +242,13 @@ class ImageServletResponseImpl extends HttpServletResponseWrapper implements Ima
|
||||
}
|
||||
else {
|
||||
super.setContentType(originalContentType);
|
||||
|
||||
ServletOutputStream out = super.getOutputStream();
|
||||
|
||||
try {
|
||||
bufferedOut.writeTo(out);
|
||||
if (bufferedOut != null) {
|
||||
bufferedOut.writeTo(out);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
out.flush();
|
||||
|
@ -0,0 +1,351 @@
|
||||
/*
|
||||
* Copyright (c) 2011, Harald Kuhr
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name "TwelveMonkeys" nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.twelvemonkeys.servlet.image;
|
||||
|
||||
import com.twelvemonkeys.io.FileUtil;
|
||||
import com.twelvemonkeys.servlet.OutputStreamAdapter;
|
||||
import com.twelvemonkeys.util.StringTokenIterator;
|
||||
import org.junit.Test;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.RenderedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* ImageFilterTestCase
|
||||
*
|
||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||
* @author last modified by $Author: haraldk$
|
||||
* @version $Id: ImageFilterTestCase.java,v 1.0 07.04.11 14.14 haraldk Exp$
|
||||
*/
|
||||
public class ImageFilterTestCase {
|
||||
|
||||
@Test
|
||||
public void passThroughIfNotTrigger() throws ServletException, IOException {
|
||||
// Filter init & setup
|
||||
ServletContext context = mock(ServletContext.class);
|
||||
|
||||
FilterConfig filterConfig = mock(FilterConfig.class);
|
||||
when(filterConfig.getFilterName()).thenReturn("dummy");
|
||||
when(filterConfig.getServletContext()).thenReturn(context);
|
||||
when(filterConfig.getInitParameterNames()).thenReturn(new StringTokenIterator("foo, bar"));
|
||||
|
||||
DummyFilter filter = new DummyFilter() {
|
||||
@Override
|
||||
protected boolean trigger(ServletRequest pRequest) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
filter.init(filterConfig);
|
||||
|
||||
// Request/response setup
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
|
||||
FilterChain chain = mock(FilterChain.class);
|
||||
|
||||
// Execute
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
// Verifications
|
||||
verify(chain).doFilter(request, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void normalFilter() throws ServletException, IOException {
|
||||
// Filter init & setup
|
||||
ServletContext context = mock(ServletContext.class);
|
||||
|
||||
FilterConfig filterConfig = mock(FilterConfig.class);
|
||||
when(filterConfig.getFilterName()).thenReturn("dummy");
|
||||
when(filterConfig.getServletContext()).thenReturn(context);
|
||||
when(filterConfig.getInitParameterNames()).thenReturn(new StringTokenIterator("foo, bar"));
|
||||
|
||||
DummyFilter filter = new DummyFilter();
|
||||
filter.init(filterConfig);
|
||||
|
||||
// Request/response setup
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
ServletOutputStream out = spy(new OutputStreamAdapter(stream));
|
||||
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
when(response.getOutputStream()).thenReturn(out);
|
||||
|
||||
FilterChain chain = mock(FilterChain.class);
|
||||
doAnswer(new Answer<Void>() {
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
HttpServletResponse response = (HttpServletResponse) invocation.getArguments()[1];
|
||||
|
||||
response.setContentType("image/png");
|
||||
response.setContentLength(104417);
|
||||
InputStream stream = getClass().getResourceAsStream("/com/twelvemonkeys/servlet/image/12monkeys-splash.png");
|
||||
assertNotNull("Missing test resource", stream);
|
||||
FileUtil.copy(stream, response.getOutputStream());
|
||||
|
||||
return null;
|
||||
}
|
||||
}).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
|
||||
|
||||
// Execute
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
// Verifications
|
||||
int length = stream.size();
|
||||
|
||||
verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
|
||||
verify(response).setContentType("image/png");
|
||||
verify(response, atMost(1)).setContentLength(length); // setContentLength not implemented, avoid future bugs
|
||||
verify(out, atLeastOnce()).flush();
|
||||
|
||||
// Extra verification here, until we come up with something better
|
||||
assertTrue(
|
||||
String.format("Unlikely length for PNG (please run manual check): %s bytes, expected about 85000 bytes", length),
|
||||
length >= 60000 && length <= 120000
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterNoContent() throws ServletException, IOException {
|
||||
// Filter init & setup
|
||||
ServletContext context = mock(ServletContext.class);
|
||||
|
||||
FilterConfig filterConfig = mock(FilterConfig.class);
|
||||
when(filterConfig.getFilterName()).thenReturn("dummy");
|
||||
when(filterConfig.getServletContext()).thenReturn(context);
|
||||
when(filterConfig.getInitParameterNames()).thenReturn(new StringTokenIterator("foo, bar"));
|
||||
|
||||
DummyFilter filter = new DummyFilter();
|
||||
filter.init(filterConfig);
|
||||
|
||||
// Request/response setup
|
||||
ServletOutputStream out = mock(ServletOutputStream.class);
|
||||
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
when(response.getOutputStream()).thenReturn(out);
|
||||
|
||||
FilterChain chain = mock(FilterChain.class);
|
||||
doAnswer(new Answer<Void>() {
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
HttpServletResponse response = (HttpServletResponse) invocation.getArguments()[1];
|
||||
|
||||
response.setContentType("image/x-imaginary");
|
||||
|
||||
return null;
|
||||
}
|
||||
}).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
|
||||
|
||||
// Execute
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
// Verifications
|
||||
verify(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
|
||||
verify(response).setContentType("image/x-imaginary");
|
||||
verify(out, atLeastOnce()).flush();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void triggerWhenTriggerParamsNull() throws ServletException {
|
||||
// Filter init & setup
|
||||
ServletContext context = mock(ServletContext.class);
|
||||
|
||||
FilterConfig filterConfig = mock(FilterConfig.class);
|
||||
when(filterConfig.getFilterName()).thenReturn("dummy");
|
||||
when(filterConfig.getServletContext()).thenReturn(context);
|
||||
when(filterConfig.getInitParameterNames()).thenReturn(new StringTokenIterator("foo, bar"));
|
||||
|
||||
DummyFilter filter = new DummyFilter();
|
||||
|
||||
filter.init(filterConfig);
|
||||
|
||||
// Execute/Verifications
|
||||
assertTrue(filter.trigger(mock(HttpServletRequest.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void triggerWithTriggerParams() throws ServletException {
|
||||
// Filter init & setup
|
||||
ServletContext context = mock(ServletContext.class);
|
||||
|
||||
FilterConfig filterConfig = mock(FilterConfig.class);
|
||||
when(filterConfig.getFilterName()).thenReturn("dummy");
|
||||
when(filterConfig.getServletContext()).thenReturn(context);
|
||||
when(filterConfig.getInitParameterNames()).thenReturn(new StringTokenIterator("triggerParams"));
|
||||
when(filterConfig.getInitParameter("triggerParams")).thenReturn("foo");
|
||||
|
||||
DummyFilter filter = new DummyFilter();
|
||||
|
||||
filter.init(filterConfig);
|
||||
|
||||
// Request/response setup
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
when(request.getParameter("foo")).thenReturn("doit");
|
||||
|
||||
|
||||
// Execute/Verifications
|
||||
assertTrue(filter.trigger(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dontTriggerWithoutTriggerParams() throws ServletException {
|
||||
// Filter init & setup
|
||||
ServletContext context = mock(ServletContext.class);
|
||||
|
||||
FilterConfig filterConfig = mock(FilterConfig.class);
|
||||
when(filterConfig.getFilterName()).thenReturn("dummy");
|
||||
when(filterConfig.getServletContext()).thenReturn(context);
|
||||
when(filterConfig.getInitParameterNames()).thenReturn(new StringTokenIterator("triggerParams"));
|
||||
when(filterConfig.getInitParameter("triggerParams")).thenReturn("foo");
|
||||
|
||||
DummyFilter filter = new DummyFilter();
|
||||
|
||||
filter.init(filterConfig);
|
||||
|
||||
// Request/response setup
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
|
||||
|
||||
// Execute/Verifications
|
||||
assertFalse(filter.trigger(request));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChaining() throws ServletException, IOException {
|
||||
// Filter init & setup
|
||||
ServletContext context = mock(ServletContext.class);
|
||||
|
||||
FilterConfig fooConfig = mock(FilterConfig.class);
|
||||
when(fooConfig.getFilterName()).thenReturn("foo");
|
||||
when(fooConfig.getServletContext()).thenReturn(context);
|
||||
when(fooConfig.getInitParameterNames()).thenReturn(new StringTokenIterator(""));
|
||||
|
||||
final AtomicReference<BufferedImage> imageRef = new AtomicReference<BufferedImage>();
|
||||
final AtomicReference<ImageServletResponse> responseRef = new AtomicReference<ImageServletResponse>();
|
||||
|
||||
DummyFilter fooFilter = new DummyFilter() {
|
||||
@Override
|
||||
protected RenderedImage doFilter(BufferedImage image, ServletRequest request, ImageServletResponse response) throws IOException {
|
||||
// NOTE: Post-filtering, this method is run after barFilter.doFilter
|
||||
assertEquals(imageRef.get(), image);
|
||||
assertEquals(responseRef.get(), response);
|
||||
|
||||
return image;
|
||||
}
|
||||
};
|
||||
fooFilter.init(fooConfig);
|
||||
|
||||
FilterConfig barConfig = mock(FilterConfig.class);
|
||||
when(barConfig.getFilterName()).thenReturn("bar");
|
||||
when(barConfig.getServletContext()).thenReturn(context);
|
||||
when(barConfig.getInitParameterNames()).thenReturn(new StringTokenIterator(""));
|
||||
|
||||
final DummyFilter barFilter = new DummyFilter() {
|
||||
@Override
|
||||
protected RenderedImage doFilter(BufferedImage image, ServletRequest request, ImageServletResponse response) throws IOException {
|
||||
// NOTE: Post-filtering, this method is run before fooFilter.doFilter
|
||||
Graphics2D graphics = image.createGraphics();
|
||||
try {
|
||||
graphics.drawRect(10, 10, 100, 100);
|
||||
}
|
||||
finally {
|
||||
graphics.dispose();
|
||||
}
|
||||
|
||||
// Store references for later, make sure this is first and only set.
|
||||
assertTrue(imageRef.compareAndSet(null, image));
|
||||
assertTrue(responseRef.compareAndSet(null, response));
|
||||
|
||||
return image;
|
||||
}
|
||||
};
|
||||
barFilter.init(barConfig);
|
||||
|
||||
// Request/response setup
|
||||
ServletOutputStream out = mock(ServletOutputStream.class);
|
||||
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||
HttpServletResponse response = mock(HttpServletResponse.class);
|
||||
when(response.getOutputStream()).thenReturn(out);
|
||||
|
||||
FilterChain chain = mock(FilterChain.class);
|
||||
final AtomicBoolean first = new AtomicBoolean(false);
|
||||
doAnswer(new Answer<Void>() {
|
||||
public Void answer(InvocationOnMock invocation) throws Throwable {
|
||||
HttpServletRequest request = (HttpServletRequest) invocation.getArguments()[0];
|
||||
HttpServletResponse response = (HttpServletResponse) invocation.getArguments()[1];
|
||||
|
||||
// Fake chaining here..
|
||||
if (first.compareAndSet(false, true)) {
|
||||
barFilter.doFilter(request, response, (FilterChain) invocation.getMock());
|
||||
return null;
|
||||
}
|
||||
|
||||
// Otherwise, fake servlet/file response
|
||||
response.setContentType("image/gif");
|
||||
InputStream stream = getClass().getResourceAsStream("/com/twelvemonkeys/servlet/image/tux.gif");
|
||||
assertNotNull("Missing test resource", stream);
|
||||
FileUtil.copy(stream, response.getOutputStream());
|
||||
|
||||
return null;
|
||||
}
|
||||
}).when(chain).doFilter(any(ServletRequest.class), any(ServletResponse.class));
|
||||
|
||||
// Execute
|
||||
fooFilter.doFilter(request, response, chain);
|
||||
|
||||
// Verifications
|
||||
verify(chain, times(2)).doFilter(any(ServletRequest.class), any(ImageServletResponse.class));
|
||||
verify(response).setContentType("image/gif");
|
||||
verify(out, atLeastOnce()).flush();
|
||||
|
||||
// NOTE:
|
||||
// We verify that the image is the same in both ImageFilter implementations, to make sure the image is only
|
||||
// decoded once, then encoded once
|
||||
}
|
||||
|
||||
private static class DummyFilter extends ImageFilter {
|
||||
@Override
|
||||
protected RenderedImage doFilter(BufferedImage image, ServletRequest request, ImageServletResponse response) throws IOException {
|
||||
return image;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user