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.graph.DAGNode;
23  import org.grouplens.grapht.reflect.Desires;
24  import org.grouplens.grapht.solver.DependencySolver;
25  import org.junit.Test;
26  
27  import javax.inject.Inject;
28  import java.io.ByteArrayInputStream;
29  import java.io.InputStream;
30  
31  import static org.hamcrest.Matchers.*;
32  import static org.junit.Assert.assertThat;
33  
34  /**
35   * Test the dependency solver's graph rewriting capabilities.
36   * @author <a href="http://www.grouplens.org">GroupLens Research</a>
37   */
38  public class GraphRewritingTest {
39      @Test
40      public void testSimpleRewriteNoTrigger() throws ResolutionException {
41          BindingFunctionBuilder config = new BindingFunctionBuilder();
42          config.getRootContext()
43                  .bind(I.class)
44                  .to(C.class);
45          config.getRootContext()
46                .bind(I2.class)
47                .to(A.class);
48          DependencySolver initial =
49                  DependencySolver.newBuilder()
50                                  .addBindingFunction(config.build(BindingFunctionBuilder.RuleSet.EXPLICIT), false)
51                                  .build();
52          initial.resolve(Desires.create(null, I.class, false));
53          DAGNode<Component, Dependency> graph = initial.getGraph();
54          assertThat(graph.getOutgoingEdges(), hasSize(1));
55          assertThat(graph.getOutgoingEdges().iterator().next()
56                          .getTail().getLabel().getSatisfaction().getErasedType(),
57                     equalTo((Class) C.class));
58          DAGNode<Component, Dependency> graph2 = initial.rewrite(graph);
59          assertThat(graph2, sameInstance(graph));
60      }
61  
62      @Test
63      public void testRewriteToIdentical() throws ResolutionException {
64          BindingFunctionBuilder config = new BindingFunctionBuilder();
65          config.getRootContext()
66                .bind(I.class)
67                .to(C.class);
68          config.getRootContext()
69                .bind(I2.class)
70                .to(A.class);
71          DependencySolver initial =
72                  DependencySolver.newBuilder()
73                                  .addBindingFunction(config.build(BindingFunctionBuilder.RuleSet.EXPLICIT), true)
74                                  .build();
75          initial.resolve(Desires.create(null, I.class, false));
76          DAGNode<Component, Dependency> graph = initial.getGraph();
77          assertThat(graph.getOutgoingEdges(), hasSize(1));
78          assertThat(graph.getOutgoingEdges().iterator().next()
79                          .getTail().getLabel().getSatisfaction().getErasedType(),
80                     equalTo((Class) C.class));
81          DAGNode<Component, Dependency> graph2 = initial.rewrite(graph);
82          // should trigger a rewrite, but the graph should be unchanged
83          assertThat(graph2, sameInstance(graph));
84      }
85  
86      @Test
87      public void testRewriteDependency() throws ResolutionException {
88          BindingFunctionBuilder config = new BindingFunctionBuilder();
89          config.getRootContext()
90                .bind(I.class)
91                .to(C.class);
92          config.getRootContext()
93                .bind(I2.class)
94                .to(A.class);
95          DependencySolver initial =
96                  DependencySolver.newBuilder()
97                                  .addBindingFunction(config.build(BindingFunctionBuilder.RuleSet.EXPLICIT))
98                                  .build();
99          initial.resolve(Desires.create(null, I.class, false));
100         DAGNode<Component, Dependency> graph = initial.getGraph();
101         assertThat(graph.getOutgoingEdges(), hasSize(1));
102         assertThat(graph.getOutgoingEdges().iterator().next()
103                         .getTail().getLabel().getSatisfaction().getErasedType(),
104                    equalTo((Class) C.class));
105         assertThat(graph.getOutgoingEdges().iterator().next()
106                         .getTail().getOutgoingEdges().iterator().next()
107                         .getTail().getLabel().getSatisfaction().getErasedType(),
108                    equalTo((Class) A.class));
109 
110         BindingFunctionBuilder config2 = new BindingFunctionBuilder();
111         config2.getRootContext()
112                .bind(I2.class)
113                .to(B.class);
114         DependencySolver rewriter =
115                 DependencySolver.newBuilder()
116                                 .addBindingFunction(config2.build(BindingFunctionBuilder.RuleSet.EXPLICIT), true)
117                                 .build();
118 
119         DAGNode<Component, Dependency> graph2 = rewriter.rewrite(graph);
120         // should change the dependency
121         assertThat(graph2, not(sameInstance(graph)));
122         assertThat(graph2.getOutgoingEdges(), hasSize(1));
123         assertThat(graph2.getOutgoingEdges().iterator().next()
124                          .getTail().getLabel().getSatisfaction().getErasedType(),
125                    equalTo((Class) C.class));
126         assertThat(graph2.getOutgoingEdges().iterator().next()
127                          .getTail().getOutgoingEdges().iterator().next()
128                          .getTail().getLabel().getSatisfaction().getErasedType(),
129                    equalTo((Class) B.class));
130     }
131 
132     @Test
133     public void testRewriteNoTrigger() throws ResolutionException {
134         BindingFunctionBuilder config = new BindingFunctionBuilder();
135         config.getRootContext()
136               .bind(I.class)
137               .to(C.class);
138         config.getRootContext()
139               .bind(I2.class)
140               .to(A.class);
141         DependencySolver initial =
142                 DependencySolver.newBuilder()
143                                 .addBindingFunction(config.build(BindingFunctionBuilder.RuleSet.EXPLICIT))
144                                 .build();
145         initial.resolve(Desires.create(null, I.class, false));
146         DAGNode<Component, Dependency> graph = initial.getGraph();
147         assertThat(graph.getOutgoingEdges(), hasSize(1));
148         assertThat(graph.getOutgoingEdges().iterator().next()
149                         .getTail().getLabel().getSatisfaction().getErasedType(),
150                    equalTo((Class) C.class));
151         assertThat(graph.getOutgoingEdges().iterator().next()
152                         .getTail().getOutgoingEdges().iterator().next()
153                         .getTail().getLabel().getSatisfaction().getErasedType(),
154                    equalTo((Class) A.class));
155 
156         BindingFunctionBuilder trigger = new BindingFunctionBuilder();
157         trigger.getRootContext()
158                .bind(I2.class)
159                .to(W.class);
160         BindingFunctionBuilder rewriteDeps = new BindingFunctionBuilder();
161         trigger.getRootContext()
162                .bind(InputStream.class)
163                .to(new ByteArrayInputStream("foo".getBytes()));
164         DependencySolver rewriter =
165                 DependencySolver.newBuilder()
166                                 .addBindingFunction(trigger.build(BindingFunctionBuilder.RuleSet.EXPLICIT), true)
167                                 .addBindingFunction(rewriteDeps.build(BindingFunctionBuilder.RuleSet.EXPLICIT), false)
168                                 .build();
169 
170         DAGNode<Component, Dependency> graph2 = rewriter.rewrite(graph);
171         // should change the dependency
172         assertThat(graph2, not(sameInstance(graph)));
173         assertThat(graph2.getOutgoingEdges(), hasSize(1));
174         assertThat(graph2.getOutgoingEdges().iterator().next()
175                          .getTail().getLabel().getSatisfaction().getErasedType(),
176                    equalTo((Class) C.class));
177         assertThat(graph2.getOutgoingEdges().iterator().next()
178                          .getTail().getOutgoingEdges().iterator().next()
179                          .getTail().getLabel().getSatisfaction().getErasedType(),
180                    equalTo((Class) W.class));
181     }
182 
183     @Test
184     public void testRewriteFixed() throws ResolutionException {
185         // based on testRewriteDependency, but with a fixed binding to prevent rewrite
186         BindingFunctionBuilder config = new BindingFunctionBuilder();
187         config.getRootContext()
188               .bind(I.class)
189               .to(C.class);
190         config.getRootContext()
191               .bind(I2.class)
192               .fixed()
193               .to(A.class);
194         DependencySolver initial =
195                 DependencySolver.newBuilder()
196                                 .addBindingFunction(config.build(BindingFunctionBuilder.RuleSet.EXPLICIT))
197                                 .build();
198         initial.resolve(Desires.create(null, I.class, false));
199         DAGNode<Component, Dependency> graph = initial.getGraph();
200 
201         BindingFunctionBuilder config2 = new BindingFunctionBuilder();
202         config2.getRootContext()
203                .bind(I2.class)
204                .to(B.class);
205         DependencySolver rewriter =
206                 DependencySolver.newBuilder()
207                                 .addBindingFunction(config2.build(BindingFunctionBuilder.RuleSet.EXPLICIT), true)
208                                 .build();
209 
210         DAGNode<Component, Dependency> graph2 = rewriter.rewrite(graph);
211         // should be unchanged, because of fixed binding
212         assertThat(graph2, sameInstance(graph));
213     }
214 
215     public static interface I {}
216     public static interface I2 {}
217     public static class C implements I {
218         final I2 plug;
219 
220         @Inject
221         public C(I2 plug) {
222             this.plug = plug;
223         }
224     }
225 
226     public static class A implements I2 {}
227     public static class B implements I2 {}
228     public static class W implements I2 {
229         private final InputStream stream;
230 
231         @Inject
232         public W(InputStream s) {
233             stream = s;
234         }
235     }
236 }