1 /**
2  * Copyright (C) 2008 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.inject.grapher;
18 
19 import com.google.common.collect.ImmutableSet;
20 import com.google.inject.AbstractModule;
21 import com.google.inject.Binding;
22 import com.google.inject.Guice;
23 import com.google.inject.Inject;
24 import com.google.inject.Key;
25 import com.google.inject.Module;
26 import com.google.inject.Provider;
27 import com.google.inject.TypeLiteral;
28 import com.google.inject.name.Names;
29 import com.google.inject.spi.ConstructorBinding;
30 import com.google.inject.spi.ConvertedConstantBinding;
31 import com.google.inject.spi.Dependency;
32 import com.google.inject.spi.HasDependencies;
33 import com.google.inject.spi.InstanceBinding;
34 import com.google.inject.spi.LinkedKeyBinding;
35 import com.google.inject.spi.ProviderBinding;
36 import com.google.inject.spi.ProviderInstanceBinding;
37 import com.google.inject.spi.ProviderKeyBinding;
38 
39 import junit.framework.TestCase;
40 
41 import java.util.Collection;
42 import java.util.Set;
43 
44 /**
45  * Tests for {@link TransitiveDependencyVisitor}.
46  *
47  * @author phopkins@gmail.com (Pete Hopkins)
48  */
49 public class TransitiveDependencyVisitorTest extends TestCase {
50   private TransitiveDependencyVisitor visitor;
51 
52   @Override
setUp()53   protected void setUp() throws Exception {
54     super.setUp();
55 
56     visitor = new TransitiveDependencyVisitor();
57   }
58 
testVisitConstructor()59   public void testVisitConstructor() {
60     Binding<?> binding = getBinding(Key.get(ConstructedClass.class));
61     Collection<Key<?>> dependencies = visitor.visit((ConstructorBinding<?>) binding);
62 
63     assertDependencies(dependencies, Key.get(A.class), Key.get(B.class), Key.get(C.class),
64         Key.get(D.class));
65   }
66 
testVisitConvertedConstant()67   public void testVisitConvertedConstant() {
68     Binding<?> binding = getBinding(Key.get(Integer.class, Names.named("number")),
69         new ConvertedConstantModule());
70     Collection<Key<?>> dependencies = visitor.visit(
71         (ConvertedConstantBinding<?>) binding);
72 
73     assertDependencies(dependencies, Key.get(String.class, Names.named("number")));
74   }
75 
testVisitInstance()76   public void testVisitInstance() {
77     Binding<?> binding = getBinding(Key.get(ConstructedClass.class), new InstanceModule());
78     Collection<Key<?>> dependencies = visitor.visit(
79         (InstanceBinding<?>) binding);
80 
81     // Dependencies will only be on the field- and method-injected classes.
82     assertDependencies(dependencies, Key.get(A.class), Key.get(D.class));
83   }
84 
testVisitInstance_instanceHasDependencies()85   public void testVisitInstance_instanceHasDependencies() {
86     Binding<?> binding = getBinding(Key.get(Interface.class), new HasDependenciesModule());
87     Collection<Key<?>> dependencies = visitor.visit(
88         (InstanceBinding<?>) binding);
89 
90     // Dependencies should only be on the stated
91     // HasDependencies#getDependencies() values
92     assertDependencies(dependencies, Key.get(G.class));
93   }
94 
testVisitLinkedKey()95   public void testVisitLinkedKey() {
96     Binding<?> binding = getBinding(Key.get(Interface.class), new LinkedKeyModule());
97     Collection<Key<?>> dependencies = visitor.visit((LinkedKeyBinding<?>) binding);
98 
99     // Dependency should be to the class this interface is bound to.
100     assertDependencies(dependencies, Key.get(ConstructedClass.class));
101   }
102 
testVisitProviderBinding()103   public void testVisitProviderBinding() {
104     Binding<?> binding = getBinding(Key.get(new TypeLiteral<Provider<ConstructedClass>>() {}));
105     Collection<Key<?>> dependencies = visitor.visit((ProviderBinding<?>) binding);
106 
107     assertDependencies(dependencies, Key.get(ConstructedClass.class));
108   }
109 
testVisitProviderInstance()110   public void testVisitProviderInstance() {
111     Binding<?> binding = getBinding(Key.get(ConstructedClass.class),
112         new ProviderInstanceModule());
113     Collection<Key<?>> dependencies = visitor.visit(
114         (ProviderInstanceBinding<?>) binding);
115 
116     // Dependencies will only be on the field- and method-injected classes.
117     assertDependencies(dependencies, Key.get(E.class), Key.get(F.class));
118   }
119 
testVisitProviderKey()120   public void testVisitProviderKey() {
121     Binding<?> binding = getBinding(Key.get(ConstructedClass.class), new ProviderKeyModule());
122     Collection<Key<?>> dependencies = visitor.visit((ProviderKeyBinding<?>) binding);
123 
124     // Dependency should be to the class that provides this one.
125     assertDependencies(dependencies, Key.get(ConstructedClassProvider.class));
126   }
127 
getBinding(Key<?> key, Module... modules)128   private Binding<?> getBinding(Key<?> key, Module... modules) {
129     return Guice.createInjector(modules).getBinding(key);
130   }
131 
assertDependencies(Collection<Key<?>> dependencies, Key<?>... keys)132   private void assertDependencies(Collection<Key<?>> dependencies, Key<?>... keys) {
133     assertNotNull("Dependencies should not be null", dependencies);
134     assertEquals("There should be " + keys.length + " dependencies",
135         keys.length, dependencies.size());
136 
137     for (Key<?> key : keys) {
138       assertTrue("Dependencies should contain " + key, dependencies.contains(key));
139     }
140   }
141 
142   private static class A {}
143   private static class B {}
144   private static class C {}
145   private static class D {}
146   private static class E {}
147   private static class F {}
148   private static class G {}
149 
150   private static interface Interface {}
151 
152   private static class ConstructedClass implements Interface {
153     @Inject A a;
ConstructedClass()154     ConstructedClass() {}
ConstructedClass(B b, C c)155     @Inject ConstructedClass(B b, C c) {}
setD(D d)156     @Inject void setD(D d) {}
157   }
158 
159   private static class ConstructedClassProvider implements Provider<ConstructedClass> {
160     @Inject E e;
ConstructedClassProvider()161     ConstructedClassProvider() {}
ConstructedClassProvider(A a, B b, C c)162     @Inject ConstructedClassProvider(A a, B b, C c) {}
setF(F f)163     @Inject void setF(F f) {}
164 
get()165     public ConstructedClass get() {
166       return null;
167     }
168   }
169 
170   private static class HasDependenciesClass implements Interface, HasDependencies {
171     @Inject A a;
172     @Inject B b;
173 
getDependencies()174     public Set<Dependency<?>> getDependencies() {
175       return ImmutableSet.<Dependency<?>>of(Dependency.get(Key.get(G.class)));
176     }
177   }
178 
179   private static class ConvertedConstantModule extends AbstractModule {
180     @Override
configure()181     protected void configure() {
182       bindConstant().annotatedWith(Names.named("number")).to("2008");
183     }
184   }
185 
186   private static class InstanceModule extends AbstractModule {
187     @Override
configure()188     protected void configure() {
189       bind(ConstructedClass.class).toInstance(new ConstructedClass());
190     }
191   }
192 
193   private static class LinkedKeyModule extends AbstractModule {
194     @Override
configure()195     protected void configure() {
196       bind(Interface.class).to(ConstructedClass.class);
197     }
198   }
199 
200   private static class ProviderInstanceModule extends AbstractModule {
201     @Override
configure()202     protected void configure() {
203       bind(ConstructedClass.class).toProvider(new ConstructedClassProvider());
204     }
205   }
206 
207   private static class HasDependenciesModule extends AbstractModule {
208     @Override
configure()209     protected void configure() {
210       bind(Interface.class).toInstance(new HasDependenciesClass());
211     }
212   }
213 
214   private static class ProviderKeyModule extends AbstractModule {
215     @Override
configure()216     protected void configure() {
217       bind(ConstructedClass.class).toProvider(ConstructedClassProvider.class);
218     }
219   }
220 }
221