/*** * Copyright (c) 2009 Caelum - www.caelum.com.br/opensource All rights reserved. * * 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 br.com.caelum.vraptor.view; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.when; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; import javax.servlet.ServletContext; import net.vidageek.mirror.dsl.Mirror; import net.vidageek.mirror.exception.MirrorException; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import br.com.caelum.vraptor.controller.DefaultBeanClass; import br.com.caelum.vraptor.core.DefaultReflectionProvider; import br.com.caelum.vraptor.core.ReflectionProvider; import br.com.caelum.vraptor.http.route.Router; import br.com.caelum.vraptor.proxy.JavassistProxifier; import br.com.caelum.vraptor.proxy.Proxifier; public class LinkToHandlerTest { private @Mock ServletContext context; private @Mock Router router; private LinkToHandler handler; private Method method2params; private Method method1param; private Method anotherMethod; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); this.handler = new LinkToHandler(context, router, new JavassistProxifier(), new DefaultReflectionProvider()); when(context.getContextPath()).thenReturn("/path"); this.method2params = new Mirror().on(TestController.class).reflect().method("method").withArgs(String.class, int.class); this.method1param = new Mirror().on(TestController.class).reflect().method("method").withArgs(String.class); this.anotherMethod = new Mirror().on(TestController.class).reflect().method("anotherMethod").withArgs(String.class, int.class); } @Test public void shouldThrowExceptionWhenMethodIsAmbiguous() throws Throwable { try { //${linkTo[TestController].method()} invoke(handler.get(new DefaultBeanClass(TestController.class)), "method"); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage().toLowerCase(), startsWith("ambiguous method")); } } @Test public void shouldThrowExceptionWhenUsingParametersOfWrongTypes() throws Throwable { //${linkTo[TestController].method(123)} try { invoke(handler.get(new DefaultBeanClass(TestController.class)), "method", 123); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage().toLowerCase(), startsWith("there are no methods")); } } @Test public void shouldReturnWantedUrlWithoutArgs() throws Throwable { when(router.urlFor(TestController.class, anotherMethod, new Object[2])).thenReturn("/expectedURL"); //${linkTo[TestController].anotherMethod()} String uri = invoke(handler.get(new DefaultBeanClass(TestController.class)), "anotherMethod"); assertThat(uri, is("/path/expectedURL")); } @Test public void shouldReturnWantedUrlWithoutArgsUsingPropertyAccess() throws Throwable { when(router.urlFor(TestController.class, anotherMethod, new Object[2])).thenReturn("/expectedURL"); //${linkTo[TestController].anotherMethod} String uri = invoke(handler.get(new DefaultBeanClass(TestController.class)), "getAnotherMethod"); assertThat(uri, is("/path/expectedURL")); } @Test public void shouldReturnWantedUrlWithParamArgs() throws Throwable { String a = "test"; int b = 3; when(router.urlFor(TestController.class, method2params, new Object[]{a, b})).thenReturn("/expectedURL"); //${linkTo[TestController].method('test', 3)} String uri = invoke(handler.get(new DefaultBeanClass(TestController.class)), "method", a, b); assertThat(uri, is("/path/expectedURL")); } @Test public void shouldReturnWantedUrlWithPartialParamArgs() throws Throwable { String a = "test"; when(router.urlFor(TestController.class, anotherMethod, new Object[]{a, null})).thenReturn("/expectedUrl"); //${linkTo[TestController].anotherMethod('test')} String uri = invoke(handler.get(new DefaultBeanClass(TestController.class)), "anotherMethod", a); assertThat(uri, is("/path/expectedUrl")); } @Test public void shouldReturnWantedUrlForOverrideMethodWithParamArgs() throws Throwable { String a = "test"; when(router.urlFor(SubGenericController.class, SubGenericController.class.getDeclaredMethod("method", new Class[]{String.class}), new Object[]{a})).thenReturn("/expectedURL"); //${linkTo[TestSubGenericController].method('test')}] String uri = invoke(handler.get(new DefaultBeanClass(SubGenericController.class)), "method", a); assertThat(uri, is("/path/expectedURL")); } @Test public void shouldReturnWantedUrlForOverrideMethodWithParialParamArgs() throws Throwable { String a = "test"; when(router.urlFor(SubGenericController.class, SubGenericController.class.getDeclaredMethod("anotherMethod", new Class[]{String.class, String.class}), new Object[]{a, null})).thenReturn("/expectedURL"); //${linkTo[TestSubGenericController].anotherMethod('test')}] String uri = invoke(handler.get(new DefaultBeanClass(SubGenericController.class)), "anotherMethod", a); assertThat(uri, is("/path/expectedURL")); } @Test public void shouldUseExactlyMatchedMethodIfTheMethodIsOverloaded() throws Throwable { String a = "test"; when(router.urlFor(TestController.class, method1param, a)).thenReturn("/expectedUrl"); //${linkTo[TestController].method('test')} String uri = invoke(handler.get(new DefaultBeanClass(TestController.class)), "method", a); assertThat(uri, is("/path/expectedUrl")); } @Test public void shouldThrowExceptionWhenPassingMoreArgsThanMethodSupports() throws Throwable { String a = "test"; int b = 3; String c = "anotherTest"; //${linkTo[TestController].anotherMethod('test', 3, 'anotherTest')} try { invoke(handler.get(new DefaultBeanClass(TestController.class)), "anotherMethod", a, b, c); fail(); } catch (IllegalArgumentException e) { assertThat(e.getMessage().toLowerCase(), startsWith("wrong number of arguments")); } } @Test public void shouldAvoidFrozenClassIfTwoVRaptorInstancesAreLoaded() throws Throwable { ServletContext context0 = Mockito.mock(ServletContext.class); ServletContext context1 = Mockito.mock(ServletContext.class); when(context0.getContextPath()).thenReturn(""); when(context1.getContextPath()).thenReturn("/another"); ReflectionProvider reflectionProvider = new DefaultReflectionProvider(); Proxifier proxifier = new JavassistProxifier(); LinkToHandler handler0 = new LinkToHandler(context0, router, proxifier, reflectionProvider); LinkToHandler handler1 = new LinkToHandler(context1, router, proxifier, reflectionProvider); Object object0 = handler0.get(new DefaultBeanClass(TestController.class)); assertThat(object0.getClass().getName(), containsString("$linkTo_$$")); Object object1 = handler1.get(new DefaultBeanClass(TestController.class)); assertThat(object1.getClass().getName(), containsString("$linkTo$another_$$")); } private String invoke(Object obj, String methodName, Object...args) throws Throwable { Class<?>[] types = extractTypes(args); try { Method method = null; for (int length = types.length; length >= 0; length--) { method = new Mirror().on(obj.getClass()).reflect().method(methodName) .withArgs(Arrays.copyOf(types, length)); if (method != null) break; } if (methodName.startsWith("get")) { return method.invoke(obj).toString(); } return new Mirror().on(obj).invoke().method(method).withArgs(args).toString(); } catch (MirrorException | InvocationTargetException e) { throw e.getCause() == null? e : e.getCause(); } } private Class<?>[] extractTypes(Object... args) { Class<?>[] classes = new Class<?>[args.length]; for (int i = 0; i < classes.length; i++) { classes[i] = args[i].getClass(); } return classes; } static class TestController { void method(String a, int b) { } void method(String a) { } void anotherMethod(String a, int b) { } } }