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
17package com.google.inject.grapher;
18
19import com.google.common.collect.ImmutableSet;
20import com.google.inject.AbstractModule;
21import com.google.inject.Binding;
22import com.google.inject.Guice;
23import com.google.inject.Inject;
24import com.google.inject.Key;
25import com.google.inject.Module;
26import com.google.inject.Provider;
27import com.google.inject.TypeLiteral;
28import com.google.inject.name.Names;
29import com.google.inject.spi.ConstructorBinding;
30import com.google.inject.spi.ConvertedConstantBinding;
31import com.google.inject.spi.Dependency;
32import com.google.inject.spi.HasDependencies;
33import com.google.inject.spi.InstanceBinding;
34import com.google.inject.spi.LinkedKeyBinding;
35import com.google.inject.spi.ProviderBinding;
36import com.google.inject.spi.ProviderInstanceBinding;
37import com.google.inject.spi.ProviderKeyBinding;
38
39import junit.framework.TestCase;
40
41import java.util.Collection;
42import java.util.Set;
43
44/**
45 * Tests for {@link TransitiveDependencyVisitor}.
46 *
47 * @author phopkins@gmail.com (Pete Hopkins)
48 */
49public class TransitiveDependencyVisitorTest extends TestCase {
50  private TransitiveDependencyVisitor visitor;
51
52  @Override
53  protected void setUp() throws Exception {
54    super.setUp();
55
56    visitor = new TransitiveDependencyVisitor();
57  }
58
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
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
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
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
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
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
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
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
128  private Binding<?> getBinding(Key<?> key, Module... modules) {
129    return Guice.createInjector(modules).getBinding(key);
130  }
131
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;
154    ConstructedClass() {}
155    @Inject ConstructedClass(B b, C c) {}
156    @Inject void setD(D d) {}
157  }
158
159  private static class ConstructedClassProvider implements Provider<ConstructedClass> {
160    @Inject E e;
161    ConstructedClassProvider() {}
162    @Inject ConstructedClassProvider(A a, B b, C c) {}
163    @Inject void setF(F f) {}
164
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
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
181    protected void configure() {
182      bindConstant().annotatedWith(Names.named("number")).to("2008");
183    }
184  }
185
186  private static class InstanceModule extends AbstractModule {
187    @Override
188    protected void configure() {
189      bind(ConstructedClass.class).toInstance(new ConstructedClass());
190    }
191  }
192
193  private static class LinkedKeyModule extends AbstractModule {
194    @Override
195    protected void configure() {
196      bind(Interface.class).to(ConstructedClass.class);
197    }
198  }
199
200  private static class ProviderInstanceModule extends AbstractModule {
201    @Override
202    protected void configure() {
203      bind(ConstructedClass.class).toProvider(new ConstructedClassProvider());
204    }
205  }
206
207  private static class HasDependenciesModule extends AbstractModule {
208    @Override
209    protected void configure() {
210      bind(Interface.class).toInstance(new HasDependenciesClass());
211    }
212  }
213
214  private static class ProviderKeyModule extends AbstractModule {
215    @Override
216    protected void configure() {
217      bind(ConstructedClass.class).toProvider(ConstructedClassProvider.class);
218    }
219  }
220}
221