Polish ControlFlowPointcut[Tests]

This commit is contained in:
Sam Brannen 2023-10-25 14:18:13 +02:00
parent 66aac7e359
commit c0f79ca3bf
2 changed files with 34 additions and 22 deletions

View File

@ -28,8 +28,9 @@ import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
/** /**
* Pointcut and method matcher for use in simple <b>cflow</b>-style pointcut. * Pointcut and method matcher for use as a simple <b>cflow</b>-style pointcut.
* Note that evaluating such pointcuts is 10-15 times slower than evaluating *
* <p>Note that evaluating such pointcuts is 10-15 times slower than evaluating
* normal pointcuts, but they are useful in some cases. * normal pointcuts, but they are useful in some cases.
* *
* @author Rod Johnson * @author Rod Johnson
@ -45,12 +46,12 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
@Nullable @Nullable
private final String methodName; private final String methodName;
private final AtomicInteger evaluations = new AtomicInteger(); private final AtomicInteger evaluationCount = new AtomicInteger();
/** /**
* Construct a new pointcut that matches all control flows below that class. * Construct a new pointcut that matches all control flows below the given class.
* @param clazz the clazz * @param clazz the class
*/ */
public ControlFlowPointcut(Class<?> clazz) { public ControlFlowPointcut(Class<?> clazz) {
this(clazz, null); this(clazz, null);
@ -58,9 +59,10 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
/** /**
* Construct a new pointcut that matches all calls below the given method * Construct a new pointcut that matches all calls below the given method
* in the given class. If no method name is given, matches all control flows * in the given class.
* <p>If no method name is given, the pointcut matches all control flows
* below the given class. * below the given class.
* @param clazz the clazz * @param clazz the class
* @param methodName the name of the method (may be {@code null}) * @param methodName the name of the method (may be {@code null})
*/ */
public ControlFlowPointcut(Class<?> clazz, @Nullable String methodName) { public ControlFlowPointcut(Class<?> clazz, @Nullable String methodName) {
@ -72,6 +74,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
/** /**
* Subclasses can override this for greater filtering (and performance). * Subclasses can override this for greater filtering (and performance).
* <p>The default implementation always returns {@code true}.
*/ */
@Override @Override
public boolean matches(Class<?> clazz) { public boolean matches(Class<?> clazz) {
@ -80,6 +83,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
/** /**
* Subclasses can override this if it's possible to filter out some candidate classes. * Subclasses can override this if it's possible to filter out some candidate classes.
* <p>The default implementation always returns {@code true}.
*/ */
@Override @Override
public boolean matches(Method method, Class<?> targetClass) { public boolean matches(Method method, Class<?> targetClass) {
@ -93,7 +97,7 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
@Override @Override
public boolean matches(Method method, Class<?> targetClass, Object... args) { public boolean matches(Method method, Class<?> targetClass, Object... args) {
this.evaluations.incrementAndGet(); this.evaluationCount.incrementAndGet();
for (StackTraceElement element : new Throwable().getStackTrace()) { for (StackTraceElement element : new Throwable().getStackTrace()) {
if (element.getClassName().equals(this.clazz.getName()) && if (element.getClassName().equals(this.clazz.getName()) &&
@ -105,10 +109,12 @@ public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher
} }
/** /**
* It's useful to know how many times we've fired, for optimization. * Get the number of times {@link #matches(Method, Class, Object...)} has been
* evaluated.
* <p>Useful for optimization and testing purposes.
*/ */
public int getEvaluations() { public int getEvaluations() {
return this.evaluations.get(); return this.evaluationCount.get();
} }

View File

@ -27,13 +27,15 @@ import org.springframework.beans.testfixture.beans.TestBean;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Tests for {@link ControlFlowPointcut}.
*
* @author Rod Johnson * @author Rod Johnson
* @author Chris Beams * @author Chris Beams
*/ */
public class ControlFlowPointcutTests { class ControlFlowPointcutTests {
@Test @Test
public void testMatches() { void matches() {
TestBean target = new TestBean(); TestBean target = new TestBean();
target.setAge(27); target.setAge(27);
NopInterceptor nop = new NopInterceptor(); NopInterceptor nop = new NopInterceptor();
@ -45,10 +47,12 @@ public class ControlFlowPointcutTests {
// Not advised, not under One // Not advised, not under One
assertThat(proxied.getAge()).isEqualTo(target.getAge()); assertThat(proxied.getAge()).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(0); assertThat(nop.getCount()).isEqualTo(0);
assertThat(cflow.getEvaluations()).isEqualTo(1);
// Will be advised // Will be advised
assertThat(new One().getAge(proxied)).isEqualTo(target.getAge()); assertThat(new One().getAge(proxied)).isEqualTo(target.getAge());
assertThat(nop.getCount()).isEqualTo(1); assertThat(nop.getCount()).isEqualTo(1);
assertThat(cflow.getEvaluations()).isEqualTo(2);
// Won't be advised // Won't be advised
assertThat(new One().nomatch(proxied)).isEqualTo(target.getAge()); assertThat(new One().nomatch(proxied)).isEqualTo(target.getAge());
@ -64,7 +68,7 @@ public class ControlFlowPointcutTests {
* expensive. * expensive.
*/ */
@Test @Test
public void testSelectiveApplication() { void selectiveApplication() {
TestBean target = new TestBean(); TestBean target = new TestBean();
target.setAge(27); target.setAge(27);
NopInterceptor nop = new NopInterceptor(); NopInterceptor nop = new NopInterceptor();
@ -91,24 +95,26 @@ public class ControlFlowPointcutTests {
} }
@Test @Test
public void testEqualsAndHashCode() throws Exception { void equalsAndHashCode() throws Exception {
assertThat(new ControlFlowPointcut(One.class)).isEqualTo(new ControlFlowPointcut(One.class)); assertThat(new ControlFlowPointcut(One.class)).isEqualTo(new ControlFlowPointcut(One.class));
assertThat(new ControlFlowPointcut(One.class, "getAge")).isEqualTo(new ControlFlowPointcut(One.class, "getAge")); assertThat(new ControlFlowPointcut(One.class, "getAge")).isEqualTo(new ControlFlowPointcut(One.class, "getAge"));
assertThat(new ControlFlowPointcut(One.class, "getAge").equals(new ControlFlowPointcut(One.class))).isFalse(); assertThat(new ControlFlowPointcut(One.class, "getAge")).isNotEqualTo(new ControlFlowPointcut(One.class));
assertThat(new ControlFlowPointcut(One.class).hashCode()).isEqualTo(new ControlFlowPointcut(One.class).hashCode());
assertThat(new ControlFlowPointcut(One.class, "getAge").hashCode()).isEqualTo(new ControlFlowPointcut(One.class, "getAge").hashCode()); assertThat(new ControlFlowPointcut(One.class)).hasSameHashCodeAs(new ControlFlowPointcut(One.class));
assertThat(new ControlFlowPointcut(One.class, "getAge").hashCode()).isNotEqualTo(new ControlFlowPointcut(One.class).hashCode()); assertThat(new ControlFlowPointcut(One.class, "getAge")).hasSameHashCodeAs(new ControlFlowPointcut(One.class, "getAge"));
assertThat(new ControlFlowPointcut(One.class, "getAge")).doesNotHaveSameHashCodeAs(new ControlFlowPointcut(One.class));
} }
@Test @Test
public void testToString() { void testToString() {
assertThat(new ControlFlowPointcut(One.class).toString()) assertThat(new ControlFlowPointcut(One.class)).asString()
.isEqualTo(ControlFlowPointcut.class.getName() + ": class = " + One.class.getName() + "; methodName = null"); .isEqualTo(ControlFlowPointcut.class.getName() + ": class = " + One.class.getName() + "; methodName = null");
assertThat(new ControlFlowPointcut(One.class, "getAge").toString()) assertThat(new ControlFlowPointcut(One.class, "getAge")).asString()
.isEqualTo(ControlFlowPointcut.class.getName() + ": class = " + One.class.getName() + "; methodName = getAge"); .isEqualTo(ControlFlowPointcut.class.getName() + ": class = " + One.class.getName() + "; methodName = getAge");
} }
public class One {
private static class One {
int getAge(ITestBean proxied) { int getAge(ITestBean proxied) {
return proxied.getAge(); return proxied.getAge();
} }