/*
* Grapht, an open source dependency injector.
* Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
* Copyright 2010-2014 Regents of the University of Minnesota
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.grouplens.grapht.reflect.internal;
import org.grouplens.grapht.annotation.AnnotationBuilder;
import org.grouplens.grapht.annotation.Attribute;
import org.grouplens.grapht.reflect.Desire;
import org.grouplens.grapht.reflect.InjectionPoint;
import org.grouplens.grapht.reflect.internal.types.RoleA;
import org.grouplens.grapht.reflect.internal.types.RoleB;
import org.grouplens.grapht.reflect.internal.types.RoleD;
import org.junit.Assert;
import org.junit.Test;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.hamcrest.Matchers.*;
public class InjectionPointTest {
private static <T extends Annotation> Annotation named(String name) {
return AnnotationBuilder.of(Named.class).setValue(name).build();
}
@Test
public void testAttributesLookup() throws Exception {
Constructor<CtorType> ctor = CtorType.class.getConstructor(Object.class, String.class);
ConstructorParameterInjectionPoint p1 = new ConstructorParameterInjectionPoint(ctor, 0);
ConstructorParameterInjectionPoint p2 = new ConstructorParameterInjectionPoint(ctor, 1);
// p1 has the transient attribute, p2 does not
Assert.assertNotNull(p1.getAttribute(Transient.class));
Assert.assertNull(p2.getAttribute(Transient.class));
Assert.assertEquals(1, p1.getAttributes().size());
Assert.assertEquals(0, p2.getAttributes().size());
}
@Test
public void testConstructorParameterInjectionPoint() throws Exception {
// created expected injection points
Constructor<CtorType> ctor = CtorType.class.getConstructor(Object.class, String.class);
ConstructorParameterInjectionPoint p1 = new ConstructorParameterInjectionPoint(ctor, 0);
ConstructorParameterInjectionPoint p2 = new ConstructorParameterInjectionPoint(ctor, 1);
Set<InjectionPoint> expected = new HashSet<InjectionPoint>();
expected.add(p1);
expected.add(p2);
// verify that the qualifiers and types are identified properly
Assert.assertThat(p1.getQualifier(), instanceOf(RoleA.class));
Assert.assertThat(p2.getQualifier(), instanceOf(RoleB.class));
Assert.assertThat(p1.getAttribute(Transient.class), notNullValue());
Assert.assertThat(p1.getAttributes(), contains(instanceOf(Transient.class)));
Assert.assertThat(p2.getAttributes(), hasSize(0));
Assert.assertThat(p2.getAttribute(Transient.class), nullValue());
Assert.assertEquals(Object.class, p1.getType());
Assert.assertEquals(String.class, p2.getType());
// verify nullability and transience
Assert.assertFalse(p1.isNullable());
Assert.assertTrue(p2.isNullable());
Assert.assertEquals(expected, getInjectionPoints(CtorType.class));
}
@Test
public void testSetterMethodInjectionPoint() throws Exception {
// create expected injection points
Method m1 = SetterType.class.getMethod("setA", Object.class);
Method m2 = SetterType.class.getMethod("setB", String.class);
Method m3 = SetterType.class.getMethod("setMulti", Object.class, String.class);
SetterInjectionPoint p1 = new SetterInjectionPoint(m1, 0);
SetterInjectionPoint p2 = new SetterInjectionPoint(m2, 0);
SetterInjectionPoint p3 = new SetterInjectionPoint(m3, 0);
SetterInjectionPoint p4 = new SetterInjectionPoint(m3, 1);
Set<InjectionPoint> expected = new HashSet<InjectionPoint>();
expected.add(p1);
expected.add(p2);
expected.add(p3);
expected.add(p4);
// verify that the qualifiers, types, attrs are identified properly
Assert.assertThat(p1.getQualifier(), instanceOf(RoleA.class));
Assert.assertThat(p1.getAttribute(Transient.class), notNullValue());
Assert.assertThat(p1.getAttributes(), contains(instanceOf(Transient.class)));
Assert.assertThat(p2.getQualifier(), instanceOf(RoleB.class));
Assert.assertThat(p2.getAttributes(), hasSize(0));
Assert.assertThat(p2.getAttribute(Transient.class), nullValue());
Assert.assertThat(p3.getQualifier(), nullValue());
Assert.assertThat(p3.getAttributes(), hasSize(0));
Assert.assertThat(p4.getQualifier(), instanceOf(RoleD.class));
Assert.assertThat(p4.getAttributes(), hasSize(0));
Assert.assertThat(p4.getAttribute(Transient.class), nullValue());
Assert.assertEquals(Object.class, p1.getType());
Assert.assertEquals(String.class, p2.getType());
Assert.assertEquals(Object.class, p3.getType());
Assert.assertEquals(String.class, p4.getType());
// verify nullability and transience
Assert.assertFalse(p1.isNullable());
Assert.assertFalse(p2.isNullable());
Assert.assertFalse(p3.isNullable());
Assert.assertTrue(p4.isNullable());
Assert.assertEquals(expected, getInjectionPoints(SetterType.class));
}
@Test
public void testPrimitiveBoxing() throws Exception {
Method m1 = PrimitiveType.class.getMethod("setUnboxed", int.class);
Method m2 = PrimitiveType.class.getMethod("setBoxed", Integer.class);
SetterInjectionPoint p1 = new SetterInjectionPoint(m1, 0);
SetterInjectionPoint p2 = new SetterInjectionPoint(m2, 0);
// make sure that both injection points are normalized to boxed types
Assert.assertEquals(Integer.class, p1.getType());
Assert.assertEquals(Integer.class, p2.getType());
}
@Test
public void testNamedQualifiers() throws Exception {
Constructor<NamedType> ctor = NamedType.class.getConstructor(String.class, Integer.class);
ConstructorParameterInjectionPoint p1 = new ConstructorParameterInjectionPoint(ctor, 0);
ConstructorParameterInjectionPoint p2 = new ConstructorParameterInjectionPoint(ctor, 1);
Set<InjectionPoint> expected = new HashSet<InjectionPoint>();
expected.add(p1);
expected.add(p2);
// verify that the qualifiers and types are identified properly
Assert.assertThat(p1.getQualifier(), equalTo(named("test1")));
Assert.assertThat(p2.getQualifier(), equalTo(named("test2")));
Assert.assertEquals(String.class, p1.getType());
Assert.assertEquals(Integer.class, p2.getType());
Assert.assertEquals(expected, getInjectionPoints(NamedType.class));
}
@Test
public void testFieldInjectionPoints() throws Exception {
Field f1 = FieldType.class.getField("field");
FieldInjectionPoint p1 = new FieldInjectionPoint(f1);
Set<InjectionPoint> expected = new HashSet<InjectionPoint>();
expected.add(p1);
Assert.assertEquals(expected, getInjectionPoints(FieldType.class));
}
@Test
public void testCombinedDesires() throws Exception {
// create expected injection points
Constructor<AllTypes> ctor = AllTypes.class.getConstructor(Object.class, String.class);
ConstructorParameterInjectionPoint p1 = new ConstructorParameterInjectionPoint(ctor, 0);
ConstructorParameterInjectionPoint p2 = new ConstructorParameterInjectionPoint(ctor, 1);
Method m1 = AllTypes.class.getMethod("setC", Object.class);
Method m2 = AllTypes.class.getMethod("setD", String.class);
Field f1 = AllTypes.class.getField("field");
FieldInjectionPoint p3 = new FieldInjectionPoint(f1);
SetterInjectionPoint p4 = new SetterInjectionPoint(m1, 0);
SetterInjectionPoint p5 = new SetterInjectionPoint(m2, 0);
Set<InjectionPoint> expected = new HashSet<InjectionPoint>();
expected.add(p1);
expected.add(p2);
expected.add(p3);
expected.add(p4);
expected.add(p5);
Assert.assertEquals(expected, getInjectionPoints(AllTypes.class));
}
@Test
public void testSubclassOverrides() throws Exception {
Method m1 = SubType.class.getMethod("injectMethod", Object.class);
SetterInjectionPoint p1 = new SetterInjectionPoint(m1, 0);
Set<InjectionPoint> expected = new HashSet<InjectionPoint>();
expected.add(p1);
Assert.assertEquals(expected, getInjectionPoints(SubType.class));
}
private Set<InjectionPoint> getInjectionPoints(Class<?> types) {
List<Desire> desires = ReflectionDesire.getDesires(types);
Set<InjectionPoint> points = new HashSet<InjectionPoint>();
for (Desire rd: desires) {
points.add(rd.getInjectionPoint());
}
return points;
}
@Attribute
@Retention(RetentionPolicy.RUNTIME)
public static @interface Transient { }
public static class CtorType {
@Inject
public CtorType(@Transient @RoleA Object a, @Nullable @RoleB String b) { }
// other constructor to be ignored
public CtorType() { }
}
public static class SetterType {
@Inject
public void setA(@Transient @RoleA Object a) { }
@Inject
public void setB(@RoleB String b) { }
@Inject
public boolean setMulti(Object a, @Nullable @RoleD String c) {
return false;
}
// other setter to be ignored
public void setX(Object c) { }
}
public static class FieldType {
@Inject public String field;
}
public static class AllTypes {
@Inject public String field;
@Inject
public AllTypes(@RoleA Object a, @RoleB String b) { }
@Inject
public void setC(Object c) { }
@Inject
public void setD(@RoleD String d) { }
}
public static class PrimitiveType {
@Inject
public void setUnboxed(int a) { }
@Inject
public void setBoxed(Integer a) { }
}
public static class NamedType {
@Inject
public NamedType(@Named("test1") String a, @Named("test2") Integer b) { }
}
public static class SuperType {
@Inject
public void nonInjectMethod(Object o) { }
public void injectMethod(Object o) { }
}
public static class SubType extends SuperType {
public void nonInjectMethod(Object o) { }
@Inject
public void injectMethod(Object o) { }
}
}