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.context.ContextElements;
24  import org.grouplens.grapht.context.ContextPattern;
25  import org.grouplens.grapht.context.Multiplicity;
26  import org.junit.Before;
27  import org.junit.Test;
28  
29  import javax.annotation.Nullable;
30  import javax.inject.Inject;
31  
32  import static org.hamcrest.CoreMatchers.*;
33  import static org.junit.Assert.assertThat;
34  
35  /**
36   * Test using multiple context bindings to separate components.
37   *
38   * @author <a href="http://grouplens.org">GroupLens Research</a>
39   */
40  public class ContextOverrideTest {
41      private InjectorBuilder build;
42  
43      @Before
44      public void setup() {
45          build = InjectorBuilder.create();
46      }
47  
48      /**
49       * Test bindings with a null, and a deeper context using a binding. To make things
50       * interesting, the thing tweaked is a concrete class.
51       */
52      @Test
53      public void testDeeperOverride() throws InjectionException {
54          build.within(Outer.class)
55               .bind(IPlug.class)
56               .to(PlugH.class);
57          build.within(Inner.class)
58               .bind(IPlug.class)
59               .to(PlugA.class);
60          Injector inj = build.build();
61  
62          Inner in = inj.getInstance(Inner.class);
63          assertThat(in, notNullValue());
64          assertThat(in.plug, instanceOf(PlugA.class));
65  
66          Outer out = inj.getInstance(Outer.class);
67          assertThat(out, notNullValue());
68          assertThat(out.plug, instanceOf(PlugH.class));
69          assertThat(out.inner, notNullValue());
70          assertThat(out.inner.plug, instanceOf(PlugA.class));
71      }
72  
73      /**
74       * Test bindings with a null, and a deeper context using a binding.
75       */
76      @Test
77      public void testNullDeepConcrete() throws InjectionException {
78          // this can be with or without the context
79          // use context to match target use in LensKit
80          build.within(COuter.class)
81               .bind(Plug.class)
82               .toNull();
83          build.within(CInner.class)
84               .bind(Plug.class)
85               .to(Plug.class, false);
86          Injector inj = build.build();
87  
88          CInner in = inj.getInstance(CInner.class);
89          assertThat(in, notNullValue());
90          assertThat(in.plug, notNullValue());
91  
92          COuter out = inj.getInstance(COuter.class);
93          assertThat(out, notNullValue());
94          assertThat(out.plug, nullValue());
95          assertThat(out.inner, notNullValue());
96          assertThat(out.inner.plug, notNullValue());
97          assertThat(out.inner.plug, instanceOf(Plug.class));
98      }
99  
100     /**
101      * Test bindings with a null, and a deeper context using a binding, with
102      * an anchored matcher.
103      */
104     @Test
105     public void testNullDeepConcreteAnchored() throws InjectionException {
106         // immediate binding should allow inner to get a plug
107         build.at(COuter.class)
108              .bind(Plug.class)
109              .toNull();
110         Injector inj = build.build();
111 
112         CInner in = inj.getInstance(CInner.class);
113         assertThat(in, notNullValue());
114         assertThat(in.plug, notNullValue());
115 
116         COuter out = inj.getInstance(COuter.class);
117         assertThat(out, notNullValue());
118         assertThat(out.plug, nullValue());
119         assertThat(out.inner, notNullValue());
120         assertThat(out.inner.plug, notNullValue());
121         assertThat(out.inner.plug, instanceOf(Plug.class));
122     }
123 
124     /**
125      * Test anchored root binding.
126      */
127     @Test
128     public void testAnchoredToRoot() throws InjectionException {
129         build.bind(IPlug.class)
130              .to(PlugA.class);
131         build.at(null)
132              .bind(IPlug.class)
133              .to(PlugH.class);
134         Injector inj = build.build();
135 
136         // Does directly requesting a plug get us the anchored binding?
137         assertThat(inj.getInstance(IPlug.class),
138                    instanceOf(PlugH.class));
139 
140         // Is the non-anchored binding used for dependencies?
141         Outer out = inj.getInstance(Outer.class);
142         assertThat(out.plug,
143                    instanceOf(PlugA.class));
144         assertThat(out.inner.plug,
145                    instanceOf(PlugA.class));
146         assertThat(out.plug, sameInstance(out.inner.plug));
147 
148         // quick check this again, make sure nothing changed
149         assertThat(inj.getInstance(IPlug.class),
150                    instanceOf(PlugH.class));
151     }
152 
153     @Test
154     public void testPatternForPlug() throws InjectionException {
155         build.matching(ContextPattern.any()
156                                      .append(CInner.class)
157                                      .append(ContextElements.invertMatch(ContextElements.matchType(PlugW.class)),
158                                              Multiplicity.ZERO_OR_MORE))
159              .bind(Plug.class)
160              .to(PlugW.class);
161         Injector inj = build.build();
162         CInner c = inj.getInstance(CInner.class);
163         assertThat(c.plug, instanceOf(PlugW.class));
164         assert c.plug != null;
165         assertThat(((PlugW) c.plug).inner.getClass(),
166                    equalTo((Class) Plug.class));
167     }
168 
169     @DefaultImplementation(PlugA.class)
170     public static interface IPlug {}
171     public static class PlugA implements IPlug {}
172     public static class PlugH implements IPlug {}
173 
174     public static class Inner {
175         final IPlug plug;
176 
177         @Inject
178         public Inner(IPlug p) {
179             plug = p;
180         }
181     }
182 
183     public static class Outer {
184         final IPlug plug;
185         final Inner inner;
186 
187         @Inject
188         public Outer(Inner in, @Nullable IPlug p) {
189             plug = p;
190             inner = in;
191         }
192     }
193 
194     public static class Plug {}
195 
196     public static class CInner {
197         final Plug plug;
198 
199         @Inject
200         public CInner(@Nullable Plug p) {
201             plug = p;
202         }
203     }
204 
205     public static class COuter {
206         final Plug plug;
207         final CInner inner;
208 
209         @Inject
210         public COuter(CInner in, @Nullable Plug p) {
211             plug = p;
212             inner = in;
213         }
214     }
215 
216     public static class PlugW extends Plug {
217         private final Plug inner;
218 
219         @Inject
220         public PlugW(Plug wrapped) {
221             inner = wrapped;
222         }
223     }
224 }