/*
* Copyright 2002-2011 the original author or authors, or Red-Black IT Ltd, as appropriate.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.redblackit.web.server.mvc;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.apache.log4j.Logger;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.springframework.core.MethodParameter;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import com.redblackit.web.server.mvc.annotation.RequestUrl;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* Test class for RequestUrlArgumentResolver, verifying correct parameter
* objects are returned for supported parameter types
*
* @author Dominic North
*
*/
@RunWith(Parameterized.class)
public class RequestUrlHandlerMethodArgumentResolverTest {
/**
* Interface for test classes
*/
interface TestController<PARMTYPE> {
void methodParameter0of1(PARMTYPE requestUrl);
void methodParameter1of3(int someInt, PARMTYPE requestUrl, Model model);
};
/**
* Class used to test with un-annotated UrlChildLocation parameter
*
* @author djnorth
*/
static private class RequestUrlHelperParameterController implements
TestController<UrlChildLocation> {
public void methodParameter0of1(UrlChildLocation urlChildLocation) {
}
public void methodParameter1of3(int someInt,
UrlChildLocation urlChildLocation, Model model) {
}
};
/**
* Class used to test with annotated UrlChildLocation parameter
*
* @author djnorth
*/
static private class AnnotatedRequestUrlHelperParameterController implements
TestController<UrlChildLocation> {
public void methodParameter0of1(@RequestUrl UrlChildLocation requestUrl) {
}
public void methodParameter1of3(int someInt,
@RequestUrl UrlChildLocation requestUrl, Model model) {
}
};
/**
* Class used to test with annotated String parameter
*
* @author djnorth
*/
static private class AnnotatedStringParameterController implements
TestController<String> {
public void methodParameter0of1(@RequestUrl String requestUrl) {
}
public void methodParameter1of3(int someInt,
@RequestUrl String requestUrl, Model model) {
}
};
/**
* Class used to test with annotated StringBuffer parameter
*
* @author djnorth
*/
static private class AnnotatedStringBufferParameterController implements
TestController<StringBuffer> {
public void methodParameter0of1(@RequestUrl StringBuffer requestUrl) {
}
public void methodParameter1of3(int someInt,
@RequestUrl StringBuffer requestUrl, Model model) {
}
};
/**
* Class used to test with annotated StringBuilder parameter
*
* @author djnorth
*/
static private class AnnotatedStringBuilderParameterController implements
TestController<StringBuilder> {
public void methodParameter0of1(@RequestUrl StringBuilder requestUrl) {
}
public void methodParameter1of3(int someInt,
@RequestUrl StringBuilder requestUrl, Model model) {
}
};
/**
* Class used to test with annotated URL parameter
*
* @author djnorth
*/
static private class AnnotatedURLParameterController implements
TestController<URL> {
public void methodParameter0of1(@RequestUrl URL requestUrl) {
}
public void methodParameter1of3(int someInt,
@RequestUrl URL requestUrl, Model model) {
}
};
/**
* Class used to test with annotated URI parameter
*
* @author djnorth
*/
static private class AnnotatedURIParameterController implements
TestController<URI> {
public void methodParameter0of1(@RequestUrl URI requestUrl) {
}
public void methodParameter1of3(int someInt,
@RequestUrl URI requestUrl, Model model) {
}
};
/**
* Class used to test with annotated Integer parameter (bad!!!)
*
* @author djnorth
*/
static private class AnnotatedIntegerParameterController implements
TestController<Integer> {
public void methodParameter0of1(@RequestUrl Integer requestUrl) {
}
public void methodParameter1of3(int someInt,
@RequestUrl Integer requestUrl, Model model) {
}
};
/**
* Class used to test with non-annotated String parameter (OK)
*
* @author djnorth
*/
static private class NonAnnotatedStringParameterController implements
TestController<String> {
public void methodParameter0of1(String requestUrl) {
}
public void methodParameter1of3(int someInt, String requestUrl,
Model model) {
}
};
/**
* Class used to test with non-annotated Integer parameter (OK)
*
* @author djnorth
*/
static private class NonAnnotatedIntegerParameterController implements
TestController<Integer> {
public void methodParameter0of1(Integer requestUrl) {
}
public void methodParameter1of3(int someInt, Integer requestUrl,
Model model) {
}
};
/**
* Test parameter creation
*/
@Parameters
public static List<Object[]> getParameters() throws Exception {
final String url0 = "https://test.com/url0";
final String url1 = "https://test.com:8443/ur10";
Object[][] parameters = {
{ new TestParameters(UrlChildLocation.class, true, true, url0,
new UrlChildLocation(url0),
new RequestUrlHelperParameterController()) },
{ new TestParameters(UrlChildLocation.class, true, true, url0,
new UrlChildLocation(url0),
new AnnotatedRequestUrlHelperParameterController()) },
{ new TestParameters(String.class, true, true, url0, url0,
new AnnotatedStringParameterController()) },
{ new TestParameters(StringBuffer.class, true, true, url0,
url0, new AnnotatedStringBufferParameterController()) },
{ new TestParameters(StringBuilder.class, true, true, url0,
url0, new AnnotatedStringBuilderParameterController()) },
{ new TestParameters(URL.class, true, true, url0,
new URL(url0), new AnnotatedURLParameterController()) },
{ new TestParameters(URI.class, true, true, url0,
new URI(url0), new AnnotatedURIParameterController()) },
{ new TestParameters(Integer.class, true, false, url0, url0,
new AnnotatedIntegerParameterController()) },
{ new TestParameters(String.class, false, true, url0, null,
new NonAnnotatedStringParameterController()) },
{ new TestParameters(Integer.class, false, false, url0, null,
new NonAnnotatedIntegerParameterController()) },
{ new TestParameters(UrlChildLocation.class, true, true, url1,
new UrlChildLocation(url1),
new RequestUrlHelperParameterController()) },
{ new TestParameters(UrlChildLocation.class, true, true, url1,
new UrlChildLocation(url1),
new AnnotatedRequestUrlHelperParameterController()) },
{ new TestParameters(String.class, true, true, url1, url1,
new AnnotatedStringParameterController()) },
{ new TestParameters(StringBuffer.class, true, true, url1,
url1, new AnnotatedStringBufferParameterController()) },
{ new TestParameters(StringBuilder.class, true, true, url1,
url1, new AnnotatedStringBuilderParameterController()) },
{ new TestParameters(URL.class, true, true, url1,
new URL(url1), new AnnotatedURLParameterController()) },
{ new TestParameters(URI.class, true, true, url1,
new URI(url1), new AnnotatedURIParameterController()) },
{ new TestParameters(Integer.class, true, false, url1, url1,
new AnnotatedIntegerParameterController()) },
{ new TestParameters(String.class, false, true, url1, null,
new NonAnnotatedStringParameterController()) },
{ new TestParameters(Integer.class, false, false, url1, null,
new NonAnnotatedIntegerParameterController()) } };
return Arrays.asList(parameters);
}
/**
* Logger
*/
private final Logger logger = Logger.getLogger("web.server");
/**
* Test parameters
*/
private TestParameters testParameters;
/**
* WebRequest for tests
*/
protected NativeWebRequest nativeWebRequest;
/**
* Wrapped HttpServletRequest
*/
protected HttpServletRequest httpServletRequest;
/**
* Object under test
*/
private RequestUrlHandlerMethodArgumentResolver resolver;
/**
* Mock ModelAndViewContainer
*/
private ModelAndViewContainer mavContainer;
/**
* Mock WebDataBinderFactory
*/
private WebDataBinderFactory webDataBinderFactory;
/**
* @param testParameters
*/
public RequestUrlHandlerMethodArgumentResolverTest(TestParameters testParameters) {
super();
this.testParameters = testParameters;
this.resolver = new RequestUrlHandlerMethodArgumentResolver();
}
/**
* Setup our mocks
*/
@Before
public void setupMocks() {
httpServletRequest = EasyMock.createMock(HttpServletRequest.class);
nativeWebRequest = EasyMock.createMock(NativeWebRequest.class);
webDataBinderFactory = EasyMock.createMock(WebDataBinderFactory.class);
if (testParameters.isParameterSupported()
&& testParameters.isParameterTypeValid()) {
EasyMock.expect(httpServletRequest.getRequestURL()).andReturn(
new StringBuffer(testParameters.requestUrl));
EasyMock.expect(
nativeWebRequest.getNativeRequest(HttpServletRequest.class))
.andReturn(httpServletRequest);
}
EasyMock.replay(httpServletRequest, nativeWebRequest, webDataBinderFactory);
mavContainer = new ModelAndViewContainer();
}
/**
* Test call using parameter 0
*
* Test method for
* {@link com.redblackit.web.server.mvc.RequestUrlHandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory)}
*/
@Test
public void testResolveArgumentParm0of1() throws Exception {
Method method = null;
try {
method = testParameters.testController.getClass().getMethod(
"methodParameter0of1", testParameters.parameterType);
} catch (NoSuchMethodException nsme) {
logger.fatal(assertMsg("controller method", null));
throw nsme;
}
verifyParameter(method, 0);
}
/**
* Test call using parameter 1
*
* Test method for
* {@link com.redblackit.web.server.mvc.RequestUrlHandlerMethodArgumentResolver#resolveArgument(org.springframework.core.MethodParameter, org.springframework.web.method.support.ModelAndViewContainer, org.springframework.web.context.request.NativeWebRequest, org.springframework.web.bind.support.WebDataBinderFactory)}
*/
@Test
public void testResolveArgumentParm1of3() throws Exception {
Method method = null;
try {
method = testParameters.testController.getClass().getMethod(
"methodParameter1of3", Integer.TYPE,
testParameters.parameterType, Model.class);
} catch (NoSuchMethodException nsme) {
logger.fatal(assertMsg("controller method", null));
throw nsme;
}
verifyParameter(method, 1);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append("RequestUrlHandlerMethodArgumentResolverTest");
sb.append("{resolver=").append(resolver);
sb.append(", mavContainer=").append(mavContainer);
sb.append(", webDataBinderFactory=").append(webDataBinderFactory);
sb.append(", testParameters=").append(testParameters);
sb.append(", nativeWebRequest=").append(nativeWebRequest);
sb.append(", httpServletRequest=").append(httpServletRequest);
sb.append('}');
return sb.toString();
}
/**
* Common method for validating method under test
*
* @param method
* @param index
* @throws Exception
*/
protected void verifyParameter(Method method, int index) throws Exception {
MethodParameter methodParameter = new MethodParameter(method, index);
Object parameterValue = null;
try {
parameterValue = invokeResolveArgument(methodParameter);
if (testParameters.isParameterSupported()) {
Assert.assertTrue(
assertMsg(
"invalid parameter type on supported parameter should cause exception",
parameterValue), testParameters
.isParameterTypeValid());
}
if (testParameters.getExpectedParameterValue() == null) {
Assert.assertNull(
assertMsg("unresolved parameter value", parameterValue),
parameterValue);
} else {
Assert.assertEquals(
assertMsg("parameter type", parameterValue),
testParameters.getExpectedParameterType(),
parameterValue.getClass());
if (testParameters.getExpectedParameterValue() instanceof String
&& !(parameterValue instanceof String)) {
// Sadly the inventors of Java did not implement equals for
// either
// StringBuffer or StringBuilder (grrrh !!!!)
Assert.assertEquals(
assertMsg("parameter value", parameterValue),
testParameters.expectedParameterValue,
parameterValue.toString());
} else {
Assert.assertEquals(
assertMsg("parameter value", parameterValue),
testParameters.getExpectedParameterValue(),
parameterValue);
}
}
verifyExpectations(parameterValue);
} catch (IllegalStateException ise) {
if (testParameters.parameterTypeValid) {
logger.fatal(assertMsg("exception on valid parameter type",
null));
verifyExpectations(parameterValue);
throw ise;
} else {
logger.trace(
assertMsg(
"expected exception on invalid parameter type",
null), ise);
verifyExpectations(parameterValue);
}
}
}
/**
* Helper to add context to assertion message
*
* @param msg to enhance
* @param parameterValue
* @return result
*/
protected String assertMsg(String msg, Object parameterValue) {
StringBuilder sb = new StringBuilder(msg);
sb.append(":parameterValue=").append(parameterValue);
if (parameterValue != null) {
sb.append(" (").append(parameterValue.getClass()).append(')');
}
sb.append(':').append(this);
return sb.toString();
}
/**
* @return the nativeWebRequest
*/
protected NativeWebRequest getNativeWebRequest() {
return nativeWebRequest;
}
/**
* @return the httpServletRequest
*/
protected HttpServletRequest getHttpServletRequest() {
return httpServletRequest;
}
/**
* @return the logger
*/
protected Logger getLogger() {
return logger;
}
/**
* Verify expectations in EasyMock
*
* @param parameterValue
*/
private void verifyExpectations(Object parameterValue) {
EasyMock.verify(httpServletRequest, nativeWebRequest);
ModelMap modelMap = mavContainer.getModel();
Assert.assertEquals(
assertMsg("modelAndViewContainer.modelMap.size()",
parameterValue), 0, modelMap.size());
Assert.assertNull(
assertMsg("modelAndViewContainer.getView() should be null" ,
parameterValue), mavContainer.getView());
Assert.assertNull(
assertMsg("modelAndViewContainer.getViewName() should be null",
parameterValue), mavContainer.getViewName());
EasyMock.verify(webDataBinderFactory);
}
/**
* Invoke our HandlerMethodArgumentResolver
*
* @param methodParameter
* @return parameter object
* @throws Exception
*/
private Object invokeResolveArgument(MethodParameter methodParameter)
throws Exception {
return resolver.resolveArgument(methodParameter, mavContainer,
getNativeWebRequest(), webDataBinderFactory);
}
/**
* Test parameter encapsulation class
*
* @author djnorth
*/
static final class TestParameters {
/**
* Type for parameter
*/
private final Class<?> parameterType;
/**
* Parameter is supported (or not)
*/
private final boolean parameterSupported;
/**
* Parameter type is valid (or not)
*/
private final boolean parameterTypeValid;
/**
* Expected value for requestUrl
*/
private final String requestUrl;
/**
* Expected value for parameter
*/
private final Object expectedParameterValue;
/**
* testController with whose methods we test
*/
private final TestController<?> testController;
/**
* @param parameterType
* @param requestUrl
* @param expectedParameterValue
* @param parameterSupported
* @param parameterTypeValid
* @param testController
*/
public TestParameters(Class<?> parameterType,
boolean parameterSupported, boolean parameterTypeValid,
String requestUrl, Object expectedParameterValue,
TestController<?> testController) {
super();
this.parameterType = parameterType;
this.requestUrl = requestUrl;
this.expectedParameterValue = expectedParameterValue;
this.parameterSupported = parameterSupported;
this.parameterTypeValid = parameterTypeValid;
this.testController = testController;
}
/**
* @return the expectedParameterType
*/
public Class<?> getExpectedParameterType() {
return parameterType;
}
/**
* @return the expectedParameterValue
*/
public Object getExpectedParameterValue() {
return expectedParameterValue;
}
/**
* @return the parameterSupported
*/
public boolean isParameterSupported() {
return parameterSupported;
}
/**
* @return the parameterTypeValid
*/
public boolean isParameterTypeValid() {
return parameterTypeValid;
}
/**
* @return the testController
*/
public TestController<?> getTestController() {
return testController;
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("RequestResponseParameters [expectedParameterType=");
builder.append(parameterType);
builder.append(", requestUrl=");
builder.append(requestUrl);
builder.append(", expectedParameterValue=");
builder.append(expectedParameterValue);
builder.append(", parameterSupported=");
builder.append(parameterSupported);
builder.append(", parameterTypeValid=");
builder.append(parameterTypeValid);
builder.append(", testController=");
builder.append(testController);
builder.append("]");
return builder.toString();
}
}
}