/**
* Copyright (C) 2014 Google Inc.
*
* 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.google.inject.jdk8;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.matcher.Matchers;
import junit.framework.TestCase;
import org.aopalliance.intercept.MethodInterceptor;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Tests for interception of default methods.
*
* @author cgdecker@google.com (Colin Decker)
*/
public class DefaultMethodInterceptionTest extends TestCase {
private static final AtomicInteger callCount = new AtomicInteger(0);
private static final AtomicInteger interceptedCallCount = new AtomicInteger(0);
// the interceptor's a lambda too
private final MethodInterceptor interceptor = invocation -> {
interceptedCallCount.incrementAndGet();
return invocation.proceed();
};
@Override
protected void setUp() throws Exception {
callCount.set(0);
interceptedCallCount.set(0);
}
@Retention(RUNTIME)
@Target({METHOD, TYPE})
public @interface InterceptMe {}
/** Interface with a default method annotated to be intercepted. */
public interface Foo {
@InterceptMe
default String defaultMethod() {
callCount.incrementAndGet();
return "Foo";
}
}
/** Foo implementation that does not override the default method. */
public static class NonOverridingFoo implements Foo {
public String methodCallingDefault() {
return "NonOverriding-" + defaultMethod();
}
}
public void testInterceptedDefaultMethod() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.annotatedWith(InterceptMe.class),
interceptor);
bind(Foo.class).to(NonOverridingFoo.class);
}
});
Foo foo = injector.getInstance(Foo.class);
assertEquals("Foo", foo.defaultMethod());
assertEquals(1, callCount.get());
assertEquals(1, interceptedCallCount.get());
}
public void testInterceptedDefaultMethod_calledByAnotherMethod() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.annotatedWith(InterceptMe.class),
interceptor);
}
});
NonOverridingFoo foo = injector.getInstance(NonOverridingFoo.class);
assertEquals("NonOverriding-Foo", foo.methodCallingDefault());
assertEquals(1, callCount.get());
assertEquals(1, interceptedCallCount.get());
}
/** A base class defining a method with the same signature as Foo's default method. */
public static class BaseClass {
// the definition of this method on the class will win over the default method
public String defaultMethod() {
callCount.incrementAndGet();
return "BaseClass";
}
}
/** Foo implementation that should use superclass method rather than default method. */
public static class InheritingFoo extends BaseClass implements Foo {
}
public void testInterceptedDefaultMethod_whenParentClassDefinesNonInterceptedMethod() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.annotatedWith(InterceptMe.class),
interceptor);
bind(Foo.class).to(InheritingFoo.class);
}
});
// the concrete implementation that wins is not annotated
Foo foo = injector.getInstance(Foo.class);
assertEquals("BaseClass", foo.defaultMethod());
assertEquals(1, callCount.get());
assertEquals(0, interceptedCallCount.get());
}
/**
* A base class defining an intercepted method with the same signature as Foo's default method.
*/
public static class BaseClass2 {
// the definition of this method on the class will win over the default method
@InterceptMe
public String defaultMethod() {
callCount.incrementAndGet();
return "BaseClass2";
}
}
/**
* Foo implementation that should use intercepted superclass method rather than default method.
*/
public static class InheritingFoo2 extends BaseClass2 implements Foo {
}
public void testInterceptedDefaultMethod_whenParentClassDefinesInterceptedMethod() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bindInterceptor(Matchers.any(), Matchers.annotatedWith(InterceptMe.class),
interceptor);
bind(Foo.class).to(InheritingFoo2.class);
}
});
// the concrete implementation that wins is not annotated
Foo foo = injector.getInstance(Foo.class);
assertEquals("BaseClass2", foo.defaultMethod());
assertEquals(1, callCount.get());
assertEquals(1, interceptedCallCount.get());
}
public interface Baz {
default String doSomething() {
return "Baz";
}
String doSomethingElse();
}
public static class BazImpl implements Baz {
@Override
public String doSomethingElse() {
return "BazImpl";
}
}
public void testInterception_ofAllMethodsOnType() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bindInterceptor(Matchers.subclassesOf(Baz.class), Matchers.any(), interceptor);
bind(Baz.class).to(BazImpl.class);
}
});
Baz baz = injector.getInstance(Baz.class);
assertEquals("Baz", baz.doSomething());
assertEquals("BazImpl", baz.doSomethingElse());
assertEquals(2, interceptedCallCount.get());
}
public void testInterception_ofAllMethodsOnType_interceptsInheritedDefaultMethod() {
Injector injector = Guice.createInjector(new AbstractModule() {
@Override
protected void configure() {
bindInterceptor(Matchers.subclassesOf(BazImpl.class), Matchers.any(), interceptor);
bind(Baz.class).to(BazImpl.class);
}
});
Baz baz = injector.getInstance(Baz.class);
assertEquals("Baz", baz.doSomething());
assertEquals("BazImpl", baz.doSomethingElse());
assertEquals(2, interceptedCallCount.get());
}
}