View Javadoc

1   /*
2    * Grapht, an open source dependency injector.
3    * Copyright 2014-2015 various contributors (see CONTRIBUTORS.txt)
4    * Copyright 2010-2014 Regents of the University of Minnesota
5    *
6    * This program is free software; you can redistribute it and/or modify
7    * it under the terms of the GNU Lesser General Public License as
8    * published by the Free Software Foundation; either version 2.1 of the
9    * License, or (at your option) any later version.
10   *
11   * This program is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13   * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14   * details.
15   *
16   * You should have received a copy of the GNU General Public License along with
17   * this program; if not, write to the Free Software Foundation, Inc., 51
18   * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19   */
20  package org.grouplens.grapht.reflect.internal;
21  
22  import com.google.common.collect.Maps;
23  import com.google.common.collect.SetMultimap;
24  import org.grouplens.grapht.*;
25  import org.grouplens.grapht.BindingFunctionBuilder.RuleSet;
26  import org.grouplens.grapht.graph.DAGEdge;
27  import org.grouplens.grapht.graph.DAGNode;
28  import org.grouplens.grapht.reflect.Desire;
29  import org.grouplens.grapht.reflect.Desires;
30  import org.grouplens.grapht.reflect.InjectionPoint;
31  import org.grouplens.grapht.reflect.internal.types.*;
32  import org.grouplens.grapht.solver.DefaultDesireBindingFunction;
33  import org.grouplens.grapht.solver.DefaultInjector;
34  import org.junit.Assert;
35  import org.junit.Test;
36  
37  import javax.inject.Provider;
38  import java.util.Map;
39  
40  import static org.hamcrest.Matchers.hasKey;
41  import static org.hamcrest.Matchers.hasSize;
42  import static org.junit.Assert.assertThat;
43  
44  public class ReflectionInjectionTest {
45      @Test
46      public void testProviderCycleInjection() throws Exception {
47          InjectorBuilder b = InjectorBuilder.create().setProviderInjectionEnabled(true);
48          Injector i = b.build();
49          
50          i.getInstance(CycleA.class);
51          DAGNode<Component,Dependency> root = ((DefaultInjector) i).getSolver().getGraph();
52  
53          assertThat(root.getReachableNodes(), hasSize(3 + 1));
54  
55          assertThat(root.getOutgoingEdges(), hasSize(1));
56          DAGNode<Component, Dependency> anode = root.getOutgoingEdges().iterator().next().getTail();
57          Assert.assertEquals(CycleA.class, anode.getLabel().getSatisfaction().getErasedType());
58          
59          Assert.assertEquals(1, anode.getOutgoingEdges().size());
60          DAGNode<Component, Dependency> bnode = anode.getOutgoingEdges().iterator().next().getTail();
61          Assert.assertEquals(CycleB.class, bnode.getLabel().getSatisfaction().getErasedType());
62          
63          Assert.assertEquals(1, bnode.getOutgoingEdges().size());
64          DAGNode<Component, Dependency> pnode = bnode.getOutgoingEdges().iterator().next().getTail();
65          Assert.assertEquals(Provider.class, pnode.getLabel().getSatisfaction().getErasedType());
66  
67          // no outgoing edges...
68          Assert.assertEquals(0, pnode.getOutgoingEdges().size());
69          // but a back edge
70          SetMultimap<DAGNode<Component,Dependency>,DAGEdge<Component, Dependency>> backEdges = ((DefaultInjector) i).getSolver().getBackEdges();
71          assertThat(backEdges.entries(), hasSize(1));
72          DAGEdge<Component, Dependency> edge = backEdges.values().iterator().next();
73          Assert.assertSame(anode, edge.getTail());
74      }
75      
76      @Test
77      public void testTypeCInjectionWithDefaults() throws Exception {
78          // Test that TypeC can be resolved successfully without any bind rules.
79          // All of TypeC's dependencies have defaults or are satisfiable.
80          Desire rootDesire = Desires.create(null, TypeC.class, false);
81          DefaultInjector r = new DefaultInjector(DefaultDesireBindingFunction.create());
82          
83          TypeC instance = r.getInstance(TypeC.class);
84          Assert.assertEquals(5, instance.getIntValue());
85          Assert.assertNotNull(instance.getInterfaceA());
86          Assert.assertTrue(instance.getInterfaceA() instanceof TypeB); // ProviderA actually creates TypeB's
87          Assert.assertSame(instance.getInterfaceA(), instance.getTypeA());
88          Assert.assertNotNull(instance.getInterfaceB());
89          Assert.assertTrue(instance.getInterfaceB() instanceof TypeB);
90          Assert.assertSame(instance.getInterfaceB(), instance.getTypeB());
91          
92          // also verify memoization
93          Assert.assertSame(instance, r.getInstance(TypeC.class));
94  
95          DAGNode<Component, Dependency> resolvedRoot =
96                  r.getSolver().getGraph().getOutgoingEdgeWithLabel(Dependency.hasInitialDesire(rootDesire)).getTail();
97          assertThat(resolvedRoot.getOutgoingEdges(),
98                     hasSize(5));
99  
100         Map<InjectionPoint, DAGNode<Component, Dependency>> deps = Maps.newHashMap();
101         for (DAGEdge<Component, Dependency> e: resolvedRoot.getOutgoingEdges()) {
102             ReflectionDesire d = (ReflectionDesire) e.getLabel().getInitialDesire();
103             
104             if (d.getInjectionPoint().equals(TypeC.CONSTRUCTOR)) {
105                 // CycleA ParameterA defaults to 5
106                 Assert.assertFalse(deps.containsKey(TypeC.CONSTRUCTOR));
107                 Assert.assertTrue(e.getTail().getLabel().getSatisfaction() instanceof InstanceSatisfaction);
108                 Assert.assertEquals(5, ((InstanceSatisfaction) e.getTail().getLabel().getSatisfaction()).getInstance());
109                 deps.put(TypeC.CONSTRUCTOR, e.getTail());
110             } else if (d.getInjectionPoint().equals(TypeC.INTERFACE_A)) {
111                 // An InterfaceA is implemented by TypeA, which is then provided by Provider CycleA
112                 Assert.assertFalse(deps.containsKey(TypeC.INTERFACE_A));
113                 Assert.assertTrue(e.getTail().getLabel().getSatisfaction() instanceof ProviderClassSatisfaction);
114                 Assert.assertEquals(ProviderA.class, ((ProviderClassSatisfaction) e.getTail().getLabel().getSatisfaction()).getProviderType());
115                 deps.put(TypeC.INTERFACE_A, e.getTail());
116             } else if (d.getInjectionPoint().equals(TypeC.TYPE_A)) {
117                 // CycleA TypeA is provided by a ProviderA
118                 Assert.assertFalse(deps.containsKey(TypeC.TYPE_A));
119                 Assert.assertTrue(e.getTail().getLabel().getSatisfaction() instanceof ProviderClassSatisfaction);
120                 Assert.assertEquals(ProviderA.class, ((ProviderClassSatisfaction) e.getTail().getLabel().getSatisfaction()).getProviderType());
121                 deps.put(TypeC.TYPE_A, e.getTail());
122             } else if (d.getInjectionPoint().equals(TypeC.INTERFACE_B)) {
123                 // RoleE inherits RoleD and that defaults to TypeB
124                 Assert.assertFalse(deps.containsKey(TypeC.INTERFACE_B));
125                 Assert.assertTrue(e.getTail().getLabel().getSatisfaction() instanceof ClassSatisfaction);
126                 Assert.assertEquals(TypeB.class, e.getTail().getLabel().getSatisfaction().getErasedType());
127                 deps.put(TypeC.INTERFACE_B, e.getTail());
128             } else if (d.getInjectionPoint().equals(TypeC.TYPE_B)) {
129                 // TypeB is satisfiable on its own
130                 Assert.assertFalse(deps.containsKey(TypeC.TYPE_B));
131                 Assert.assertTrue(e.getTail().getLabel().getSatisfaction() instanceof ClassSatisfaction);
132                 Assert.assertEquals(TypeB.class, e.getTail().getLabel().getSatisfaction().getErasedType());
133                 deps.put(TypeC.TYPE_B, e.getTail());
134             } else {
135                 Assert.fail();
136             }
137         }
138         
139         // verify that all injection points were tested
140         Assert.assertTrue(deps.containsKey(TypeC.CONSTRUCTOR));
141         Assert.assertTrue(deps.containsKey(TypeC.TYPE_A));
142         Assert.assertTrue(deps.containsKey(TypeC.INTERFACE_A));
143         Assert.assertTrue(deps.containsKey(TypeC.TYPE_B));
144         Assert.assertTrue(deps.containsKey(TypeC.INTERFACE_B));
145         
146         // make sure that nodes are shared where appropriate
147         Assert.assertSame(deps.get(TypeC.INTERFACE_A), deps.get(TypeC.TYPE_A));
148         Assert.assertSame(deps.get(TypeC.INTERFACE_B), deps.get(TypeC.TYPE_B));
149     }
150     
151     @Test
152     public void testTypeCInjectionWithBindings() throws Exception {
153         // Test that TypeC can be injected correctly using bind rules, although
154         // the bind rule configuration does not need to be very complicated, since
155         // the resolver and bind rules are already tested.
156         Desire rootDesire = Desires.create(null, TypeC.class, false);
157         
158         TypeA a = new TypeA();
159         TypeB b = new TypeB();
160         
161         BindingFunctionBuilder bindRules = new BindingFunctionBuilder(false);
162         bindRules.getRootContext().bind(Integer.class).withQualifier(ParameterA.class).to(10);
163         bindRules.getRootContext().bind(InterfaceA.class).withQualifier(RoleA.class).to(PrimeA.class);
164         bindRules.getRootContext().bind(InterfaceB.class).withQualifier(RoleD.class).to(PrimeB.class);
165         bindRules.getRootContext().bind(TypeA.class).to(a);
166         bindRules.getRootContext().bind(TypeB.class).to(b);
167         
168         DefaultInjector r = new DefaultInjector(bindRules.build(RuleSet.EXPLICIT), DefaultDesireBindingFunction.create());
169 
170         TypeC instance = r.getInstance(TypeC.class);
171         Assert.assertEquals(10, instance.getIntValue());
172         Assert.assertNotNull(instance.getInterfaceA());
173         Assert.assertTrue(instance.getInterfaceA() instanceof PrimeA);
174         Assert.assertSame(a, instance.getTypeA());
175         Assert.assertNotNull(instance.getInterfaceB());
176         Assert.assertTrue(instance.getInterfaceB() instanceof PrimeB);
177         Assert.assertSame(b, instance.getTypeB());
178         
179         // also verify memoization
180         Assert.assertSame(instance, r.getInstance(TypeC.class));
181 
182         DAGNode<Component, Dependency> resolvedRoot =
183                 r.getSolver().getGraph().getOutgoingEdgeWithLabel(Dependency.hasInitialDesire(rootDesire)).getTail();
184         assertThat(resolvedRoot.getOutgoingEdges(),
185                    hasSize(5));
186         
187         Map<InjectionPoint, DAGNode<Component, Dependency>> deps = Maps.newHashMap();
188         for (DAGEdge<Component, Dependency> e: resolvedRoot.getOutgoingEdges()) {
189             ReflectionDesire d = (ReflectionDesire) e.getLabel().getInitialDesire();
190             
191             if (d.getInjectionPoint().equals(TypeC.CONSTRUCTOR)) {
192                 // ParameterA was set to 10
193                 Assert.assertFalse(deps.containsKey(TypeC.CONSTRUCTOR));
194                 Assert.assertTrue(e.getTail().getLabel().getSatisfaction() instanceof InstanceSatisfaction);
195                 Assert.assertEquals(10, ((InstanceSatisfaction) e.getTail().getLabel().getSatisfaction()).getInstance());
196                 deps.put(TypeC.CONSTRUCTOR, e.getTail());
197             } else if (d.getInjectionPoint().equals(TypeC.INTERFACE_A)) {
198                 // An InterfaceA has been bound to PrimeA
199                 Assert.assertFalse(deps.containsKey(TypeC.INTERFACE_A));
200                 Assert.assertTrue(e.getTail().getLabel().getSatisfaction() instanceof ClassSatisfaction);
201                 Assert.assertEquals(PrimeA.class, e.getTail().getLabel().getSatisfaction().getErasedType());
202                 deps.put(TypeC.INTERFACE_A, e.getTail());
203             } else if (d.getInjectionPoint().equals(TypeC.TYPE_A)) {
204                 // CycleA TypeA has been bound to an instance
205                 Assert.assertFalse(deps.containsKey(TypeC.TYPE_A));
206                 Assert.assertTrue(e.getTail().getLabel().getSatisfaction() instanceof InstanceSatisfaction);
207                 Assert.assertSame(a, ((InstanceSatisfaction) e.getTail().getLabel().getSatisfaction()).getInstance());
208                 deps.put(TypeC.TYPE_A, e.getTail());
209             } else if (d.getInjectionPoint().equals(TypeC.INTERFACE_B)) {
210                 // RoleE has been bound to PrimeB
211                 Assert.assertFalse(deps.containsKey(TypeC.INTERFACE_B));
212                 Assert.assertTrue(e.getTail().getLabel().getSatisfaction() instanceof ClassSatisfaction);
213                 Assert.assertEquals(PrimeB.class, e.getTail().getLabel().getSatisfaction().getErasedType());
214                 deps.put(TypeC.INTERFACE_B, e.getTail());
215             } else if (d.getInjectionPoint().equals(TypeC.TYPE_B)) {
216                 // TypeB has been bound to an instance
217                 Assert.assertFalse(deps.containsKey(TypeC.TYPE_B));
218                 Assert.assertTrue(e.getTail().getLabel().getSatisfaction() instanceof InstanceSatisfaction);
219                 Assert.assertSame(b, ((InstanceSatisfaction) e.getTail().getLabel().getSatisfaction()).getInstance());
220                 deps.put(TypeC.TYPE_B, e.getTail());
221             } else {
222                 Assert.fail();
223             }
224         }
225         
226         // verify that all injection points were tested
227         Assert.assertTrue(deps.containsKey(TypeC.CONSTRUCTOR));
228         Assert.assertTrue(deps.containsKey(TypeC.TYPE_A));
229         Assert.assertTrue(deps.containsKey(TypeC.INTERFACE_A));
230         Assert.assertTrue(deps.containsKey(TypeC.TYPE_B));
231         Assert.assertTrue(deps.containsKey(TypeC.INTERFACE_B));
232     }
233     
234     public static class PrimeA implements InterfaceA {
235         
236     }
237     
238     public static class PrimeB implements InterfaceB {
239         
240     }
241 }