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;
21  
22  import org.grouplens.grapht.annotation.DefaultImplementation;
23  import org.grouplens.grapht.annotation.DefaultProvider;
24  import org.grouplens.grapht.solver.UnresolvableDependencyException;
25  import org.junit.Test;
26  
27  import javax.annotation.Nullable;
28  import javax.inject.Inject;
29  import javax.inject.Provider;
30  
31  import static org.hamcrest.Matchers.*;
32  import static org.junit.Assert.*;
33  
34  public class SkipIfUnusableTest {
35      /**
36       * A skippable default with satisfied dependencies should be injected.
37       */
38      @Test
39      public void testSatisfyImplementation() throws InjectionException {
40          InjectorBuilder bld = InjectorBuilder.create();
41          bld.bind(Inner.class).to(InnerObj.class);
42          Injector inj = bld.build();
43          IfaceWithSkippableDefault obj = inj.getInstance(IfaceWithSkippableDefault.class);
44          assertThat(obj, instanceOf(DftImpl.class));
45      }
46  
47      /**
48       * A skippable default provider with satisfied dependencies should be injected.
49       */
50      @Test
51      public void testSatisfyProvider() throws InjectionException {
52          InjectorBuilder bld = InjectorBuilder.create();
53          bld.bind(Inner.class).to(InnerObj.class);
54          Injector inj = bld.build();
55          IfaceWithSkippableDefaultProvider obj = inj.getInstance(IfaceWithSkippableDefaultProvider.class);
56          assertThat(obj, instanceOf(ProvidedImpl.class));
57      }
58  
59      /**
60       * A skippable default without satisfied dependencies should not be injected.
61       */
62      @Test
63      public void testSkipImplementation() {
64          InjectorBuilder bld = InjectorBuilder.create();
65          Injector inj = bld.build();
66          try {
67              IfaceWithSkippableDefault obj = inj.tryGetInstance(null, IfaceWithSkippableDefault.class);
68              assertThat(obj, nullValue());
69          } catch (InjectionException ex) {
70              fail("injection of skipped object should succeed with null object");
71          }
72      }
73  
74      /**
75       * A skippable default provider without satisfied dependencies should not be injected.
76       */
77      @Test
78      public void testSkipProvider() {
79          InjectorBuilder bld = InjectorBuilder.create();
80          Injector inj = bld.build();
81          try {
82              IfaceWithSkippableDefaultProvider obj = inj.tryGetInstance(null, IfaceWithSkippableDefaultProvider.class);
83              assertThat(obj, nullValue());
84          } catch (InjectionException e) {
85              fail("injection of skipped provider should succeed with null object");
86          }
87      }
88  
89      /**
90       * A skippable default with a satisfied dependency that itself has an unsatisfied dependency should fail.
91       */
92      @Test
93      public void testFailWithUnsatisfiedTransitiveDep() {
94          InjectorBuilder bld = InjectorBuilder.create();
95          bld.bind(Inner.class).to(InnerWithDep.class);
96          Injector inj = bld.build();
97          try {
98              inj.tryGetInstance(null, IfaceWithSkippableDefault.class);
99              fail("injecting a skippable default with a satisfied but instantiable dep should fail");
100         } catch (InjectionException ex) {
101             assertThat(ex, instanceOf(UnresolvableDependencyException.class));
102         }
103     }
104 
105     /**
106      * Injection of component that depends on a skipped default should fail.
107      */
108     @Test
109     public void testFailWithUnusableDefaultForDep() {
110         InjectorBuilder bld = InjectorBuilder.create();
111         Injector inj = bld.build();
112         try {
113             inj.getInstance(null, DefaultRequirer.class);
114             fail("injection of dep on skipped default should fail");
115         } catch (InjectionException ex) {
116             assertThat(ex, instanceOf(UnresolvableDependencyException.class));
117         }
118     }
119 
120     /**
121      * Injection of component that optionally uses a skipped default should succeed.
122      */
123     @Test
124     public void testAcceptSkippedOptionalDep() {
125         InjectorBuilder bld = InjectorBuilder.create();
126         Injector inj = bld.build();
127         try {
128             OptionalDefaultRequirer obj = inj.getInstance(OptionalDefaultRequirer.class);
129             assertThat(obj, notNullValue());
130         } catch (InjectionException ex) {
131             fail("injection of component with optional dep on skipped default should succeed");
132         }
133     }
134 
135     /**
136      * Injection of component that optionally uses a non-skipped but transitively non-instantiable default should fail.
137      */
138     @Test
139     public void testFailOptionalDepHasUnmetDep() {
140         InjectorBuilder bld = InjectorBuilder.create();
141         bld.bind(Inner.class).to(InnerWithDep.class);
142         Injector inj = bld.build();
143         try {
144             OptionalDefaultRequirer obj = inj.getInstance(OptionalDefaultRequirer.class);
145             fail("injection of component with optional dep on non-skipped but uninstantiable default should fail");
146         } catch (InjectionException ex) {
147             assertThat(ex, instanceOf(UnresolvableDependencyException.class));
148         }
149     }
150 
151     /**
152      * Skippable defaults dependend on by skippable defaults should cleanly be skipped.
153      */
154     @Test
155     public void testNestedSkipping() throws InjectionException {
156         InjectorBuilder bld = InjectorBuilder.create();
157         Injector inj = bld.build();
158         Outer obj = inj.tryGetInstance(null, Outer.class);
159         assertThat(obj, nullValue());
160     }
161 
162     /**
163      * Interface for dependencies.
164      */
165     interface Inner {
166     }
167 
168     /**
169      * Implementation of dependency interface.
170      */
171     static class InnerObj implements Inner {
172         @Inject
173         public InnerObj() {}
174     }
175 
176     /**
177      * Implementation of dependency with a dependency.
178      */
179     static class InnerWithDep implements Inner {
180         private final String message;
181 
182         @Inject
183         public InnerWithDep(String msg) {
184             message = msg;
185         }
186 
187         public String getMessage() {
188             return message;
189         }
190     }
191 
192     /**
193      * Interface with a default that is skippable.
194      */
195     @DefaultImplementation(value=DftImpl.class, skipIfUnusable = true)
196     interface IfaceWithSkippableDefault {
197         Inner getInner();
198     }
199 
200     /**
201      * Default implementation of the skippable interface.
202      */
203     static class DftImpl implements IfaceWithSkippableDefault {
204         private final Inner inner;
205 
206         @Inject
207         public DftImpl(Inner in) {
208             inner = in;
209         }
210 
211         @Override
212         public Inner getInner() {
213             return inner;
214         }
215     }
216 
217     /**
218      * Interface with a default provider that is skippable.
219      */
220     @DefaultProvider(value=DftProvider.class, skipIfUnusable = true)
221     interface IfaceWithSkippableDefaultProvider {
222         Inner getInner();
223     }
224 
225     /**
226      * Default provider for the skippable interface.
227      */
228     static class DftProvider implements Provider<IfaceWithSkippableDefaultProvider> {
229         private final Inner inner;
230 
231         @Inject
232         public DftProvider(Inner in) {
233             inner = in;
234         }
235 
236         @Override
237         public IfaceWithSkippableDefaultProvider get() {
238             return new ProvidedImpl(inner);
239         }
240     }
241 
242     static class ProvidedImpl implements IfaceWithSkippableDefaultProvider {
243         private final Inner inner;
244 
245         public ProvidedImpl(Inner in) {
246             inner = in;
247         }
248 
249         @Override
250         public Inner getInner() {
251             return inner;
252         }
253     }
254 
255     @DefaultImplementation(value = DefaultRequirer.class, skipIfUnusable = true)
256     interface Outer {}
257 
258     /**
259      * A component that requires an object with one of our skippable defaults.
260      */
261     static class DefaultRequirer implements Outer {
262         private final IfaceWithSkippableDefault dependency;
263 
264         @Inject
265         public DefaultRequirer(IfaceWithSkippableDefault dep) {
266             dependency = dep;
267         }
268 
269         public IfaceWithSkippableDefault getDependency() {
270             return dependency;
271         }
272     }
273 
274     /**
275      * A component that optionally uses an object with one of our skippable defaults.
276      */
277     static class OptionalDefaultRequirer {
278         private final IfaceWithSkippableDefault dependency;
279 
280         @Inject
281         public OptionalDefaultRequirer(@Nullable IfaceWithSkippableDefault dep) {
282             dependency = dep;
283         }
284 
285         public IfaceWithSkippableDefault getDependency() {
286             return dependency;
287         }
288     }
289 }