/*
* Copyright 2002-2006 the original author or authors.
*
* 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 org.springframework.aop.aspectj;
import junit.framework.TestCase;
import org.aspectj.lang.JoinPoint;
import org.springframework.aop.aspectj.AspectJAdviceParameterNameDiscoverer.AmbiguousBindingException;
import java.lang.reflect.Method;
/**
* Unit tests for the {@link AspectJAdviceParameterNameDiscoverer} class.
* <p/>
* <p>See also <code>TigerAspectJAdviceParameterNameDiscovererTests</code> in
* the 'tiger' tree for tests relating to annotations.
*
* @author Adrian Colyer
*/
public class AspectJAdviceParameterNameDiscovererTests extends TestCase {
// methods to discover parameter names for
public void noArgs() {
}
public void tjp(JoinPoint jp) {
}
public void tjpsp(JoinPoint.StaticPart tjpsp) {
}
public void twoJoinPoints(JoinPoint jp1, JoinPoint jp2) {
}
public void oneThrowable(Exception ex) {
}
public void jpAndOneThrowable(JoinPoint jp, Exception ex) {
}
public void jpAndTwoThrowables(JoinPoint jp, Exception ex, Error err) {
}
public void oneObject(Object x) {
}
public void twoObjects(Object x, Object y) {
}
public void onePrimitive(int x) {
}
public void oneObjectOnePrimitive(Object x, int y) {
}
public void oneThrowableOnePrimitive(Throwable x, int y) {
}
public void theBigOne(JoinPoint jp, Throwable x, int y, Object foo) {
}
public void testNoArgs() {
assertParameterNames(getMethod("noArgs"), "execution(* *(..))", new String[0]);
}
public void testJoinPointOnly() {
assertParameterNames(getMethod("tjp"), "execution(* *(..))", new String[]{"thisJoinPoint"});
}
public void testJoinPointStaticPartOnly() {
assertParameterNames(getMethod("tjpsp"), "execution(* *(..))", new String[]{"thisJoinPointStaticPart"});
}
public void testTwoJoinPoints() {
assertException(getMethod("twoJoinPoints"), "foo()", IllegalStateException.class, "Failed to bind all argument names: 1 argument(s) could not be bound");
}
public void testOneThrowable() {
assertParameterNames(getMethod("oneThrowable"), "foo()", null, "ex", new String[]{"ex"});
}
public void testOneJPAndOneThrowable() {
assertParameterNames(getMethod("jpAndOneThrowable"), "foo()", null, "ex", new String[]{"thisJoinPoint", "ex"});
}
public void testOneJPAndTwoThrowables() {
assertException(getMethod("jpAndTwoThrowables"), "foo()", null, "ex", AmbiguousBindingException.class,
"Binding of throwing parameter 'ex' is ambiguous: could be bound to argument 1 or argument 2");
}
public void testThrowableNoCandidates() {
assertException(getMethod("noArgs"), "foo()", null, "ex", IllegalStateException.class,
"Not enough arguments in method to satisfy binding of returning and throwing variables");
}
public void testReturning() {
assertParameterNames(getMethod("oneObject"), "foo()", "obj", null, new String[]{"obj"});
}
public void testAmbiguousReturning() {
assertException(getMethod("twoObjects"), "foo()", "obj", null, AmbiguousBindingException.class,
"Binding of returning parameter 'obj' is ambiguous, there are 2 candidates.");
}
public void testReturningNoCandidates() {
assertException(getMethod("noArgs"), "foo()", "obj", null, IllegalStateException.class,
"Not enough arguments in method to satisfy binding of returning and throwing variables");
}
public void testThisBindingOneCandidate() {
assertParameterNames(getMethod("oneObject"), "this(x)", new String[]{"x"});
}
public void testThisBindingWithAlternateTokenizations() {
assertParameterNames(getMethod("oneObject"), "this( x )", new String[]{"x"});
assertParameterNames(getMethod("oneObject"), "this( x)", new String[]{"x"});
assertParameterNames(getMethod("oneObject"), "this (x )", new String[]{"x"});
assertParameterNames(getMethod("oneObject"), "this(x )", new String[]{"x"});
assertParameterNames(getMethod("oneObject"), "foo() && this(x)", new String[]{"x"});
}
public void testThisBindingTwoCandidates() {
assertException(getMethod("oneObject"), "this(x) || this(y)", AmbiguousBindingException.class,
"Found 2 candidate this(), target() or args() variables but only one unbound argument slot");
}
public void testThisBindingWithBadPointcutExpressions() {
assertException(getMethod("oneObject"), "this(", IllegalStateException.class,
"Failed to bind all argument names: 1 argument(s) could not be bound");
assertException(getMethod("oneObject"), "this(x && foo()", IllegalStateException.class,
"Failed to bind all argument names: 1 argument(s) could not be bound");
}
public void testTargetBindingOneCandidate() {
assertParameterNames(getMethod("oneObject"), "target(x)", new String[]{"x"});
}
public void testTargetBindingWithAlternateTokenizations() {
assertParameterNames(getMethod("oneObject"), "target( x )", new String[]{"x"});
assertParameterNames(getMethod("oneObject"), "target( x)", new String[]{"x"});
assertParameterNames(getMethod("oneObject"), "target (x )", new String[]{"x"});
assertParameterNames(getMethod("oneObject"), "target(x )", new String[]{"x"});
assertParameterNames(getMethod("oneObject"), "foo() && target(x)", new String[]{"x"});
}
public void testTargetBindingTwoCandidates() {
assertException(getMethod("oneObject"), "target(x) || target(y)", AmbiguousBindingException.class,
"Found 2 candidate this(), target() or args() variables but only one unbound argument slot");
}
public void testTargetBindingWithBadPointcutExpressions() {
assertException(getMethod("oneObject"), "target(", IllegalStateException.class,
"Failed to bind all argument names: 1 argument(s) could not be bound");
assertException(getMethod("oneObject"), "target(x && foo()", IllegalStateException.class,
"Failed to bind all argument names: 1 argument(s) could not be bound");
}
public void testArgsBindingOneObject() {
assertParameterNames(getMethod("oneObject"), "args(x)", new String[]{"x"});
}
public void testArgsBindingOneObjectTwoCandidates() {
assertException(getMethod("oneObject"), "args(x,y)", AmbiguousBindingException.class,
"Found 2 candidate this(), target() or args() variables but only one unbound argument slot");
}
public void testAmbiguousArgsBinding() {
assertException(getMethod("twoObjects"), "args(x,y)", AmbiguousBindingException.class,
"Still 2 unbound args at this(),target(),args() binding stage, with no way to determine between them");
}
public void testArgsOnePrimitive() {
assertParameterNames(getMethod("onePrimitive"), "args(count)", new String[]{"count"});
}
public void testArgsOnePrimitiveOneObject() {
assertException(getMethod("oneObjectOnePrimitive"), "args(count,obj)", AmbiguousBindingException.class,
"Found 2 candidate variable names but only one candidate binding slot when matching primitive args");
}
public void testThisAndPrimitive() {
assertParameterNames(getMethod("oneObjectOnePrimitive"), "args(count) && this(obj)", new String[]{"obj", "count"});
}
public void testTargetAndPrimitive() {
assertParameterNames(getMethod("oneObjectOnePrimitive"), "args(count) && target(obj)", new String[]{"obj", "count"});
}
public void testThrowingAndPrimitive() {
assertParameterNames(getMethod("oneThrowableOnePrimitive"), "args(count)", null, "ex", new String[]{"ex", "count"});
}
public void testAllTogetherNow() {
assertParameterNames(getMethod("theBigOne"), "this(foo) && args(x)", null, "ex", new String[]{"thisJoinPoint", "ex", "x", "foo"});
}
public void testReferenceBinding() {
assertParameterNames(getMethod("onePrimitive"),"somepc(foo)",new String[] {"foo"});
}
public void testReferenceBindingWithAlternateTokenizations() {
assertParameterNames(getMethod("onePrimitive"),"call(bar *) && somepc(foo)",new String[] {"foo"});
assertParameterNames(getMethod("onePrimitive"),"somepc ( foo )",new String[] {"foo"});
assertParameterNames(getMethod("onePrimitive"),"somepc( foo)",new String[] {"foo"});
}
protected Method getMethod(String name) {
// assumes no overloading of test methods...
Method[] candidates = this.getClass().getMethods();
for (int i = 0; i < candidates.length; i++) {
if (candidates[i].getName().equals(name)) {
return candidates[i];
}
}
fail("Bad test specification, no method '" + name + "' found in test class");
return null;
}
protected void assertParameterNames(Method m, String pointcut, String[] parameterNames) {
assertParameterNames(m, pointcut, null, null, parameterNames);
}
protected void assertParameterNames(Method m, String pointcut, String returning, String throwing, String[] parameterNames) {
assertEquals("bad test specification, must have same number of parameter names as method arguments",
m.getParameterTypes().length, parameterNames.length);
AspectJAdviceParameterNameDiscoverer discoverer = new AspectJAdviceParameterNameDiscoverer(pointcut);
discoverer.setRaiseExceptions(true);
discoverer.setReturningName(returning);
discoverer.setThrowingName(throwing);
String[] discoveredNames = discoverer.getParameterNames(m);
String formattedExpectedNames = format(parameterNames);
String formattedActualNames = format(discoveredNames);
assertEquals("Expecting " + parameterNames.length + " parameter names in return set '" +
formattedExpectedNames + "', but found " + discoveredNames.length +
" '" + formattedActualNames + "'",
parameterNames.length, discoveredNames.length);
for (int i = 0; i < discoveredNames.length; i++) {
assertNotNull("Parameter names must never be null", discoveredNames[i]);
assertEquals("Expecting parameter " + i + " to be named '" +
parameterNames[i] + "' but was '" + discoveredNames[i] + "'",
parameterNames[i], discoveredNames[i]);
}
}
protected void assertException(Method m, String pointcut, Class exceptionType, String message) {
assertException(m, pointcut, null, null, exceptionType, message);
}
protected void assertException(Method m, String pointcut, String returning, String throwing, Class exceptionType, String message) {
AspectJAdviceParameterNameDiscoverer discoverer = new AspectJAdviceParameterNameDiscoverer(pointcut);
discoverer.setRaiseExceptions(true);
discoverer.setReturningName(returning);
discoverer.setThrowingName(throwing);
try {
discoverer.getParameterNames(m);
fail("Expecting " + exceptionType.getName() + " with message '" + message + "'");
} catch (RuntimeException expected) {
assertEquals("Expecting exception of type " + exceptionType.getName(),
exceptionType, expected.getClass());
assertEquals("Exception message does not match expected", message, expected.getMessage());
}
}
private static String format(String[] names) {
StringBuffer sb = new StringBuffer();
sb.append("(");
for (int i = 0; i < names.length; i++) {
sb.append(names[i]);
if ((i + 1) < names.length) {
sb.append(",");
}
}
sb.append(")");
return sb.toString();
}
}