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.context;
21  
22  import org.grouplens.grapht.reflect.MockInjectionPoint;
23  import org.grouplens.grapht.solver.DependencySolver;
24  import org.grouplens.grapht.solver.InjectionContext;
25  import org.grouplens.grapht.reflect.Desire;
26  import org.grouplens.grapht.reflect.Desires;
27  import org.grouplens.grapht.reflect.MockSatisfaction;
28  import org.junit.Test;
29  
30  import java.util.ArrayList;
31  
32  import static org.hamcrest.CoreMatchers.notNullValue;
33  import static org.hamcrest.CoreMatchers.nullValue;
34  import static org.hamcrest.Matchers.*;
35  import static org.junit.Assert.assertThat;
36  
37  public class ContextPatternTest {
38      @Test
39      public void testSingletonChainEmptyContextSuccess() throws Exception {
40          // Test that a singleton pattern matches an empty context
41          assertThat(ContextPattern.empty().append(ContextElements.matchAny()).matches(makeContext()),
42                     notNullValue());
43      }
44  
45      @Test
46      public void testAnyChainEmptyContextSuccess() throws Exception {
47          // Test that the 'any' pattern matches an empty context
48          assertThat(ContextPattern.any().matches(makeContext()),
49                     notNullValue());
50      }
51  
52      @Test
53      public void testEmptyChainNonEmptyContextFailure() throws Exception {
54          assertThat(ContextPattern.empty().matches(makeContext(A.class)),
55                     nullValue());
56      }
57  
58      @Test
59      public void testSingleton() {
60          // test a single type pattern
61          // should match itself
62          ContextPattern initial = ContextPattern.empty().append(ContextElements.matchAny());
63          assertThat(initial.append(A.class).matches(makeContext(A.class)),
64                     notNullValue());
65          // should not match other type
66          assertThat(initial.append(A.class).matches(makeContext(B.class)),
67                     nullValue());
68          // should not match empty
69          assertThat(initial.append(A.class).matches(makeContext()),
70                     nullValue());
71          // should not match too long
72          assertThat(initial.append(A.class).matches(makeContext(A.class, B.class)),
73                     nullValue());
74          // either way
75          assertThat(initial.append(A.class).matches(makeContext(B.class, A.class)),
76                     nullValue());
77          // either way
78          assertThat(initial.append(A.class).matches(makeContext(B.class, A.class)),
79                     nullValue());
80          // or duplicated
81          assertThat(initial.append(A.class).matches(makeContext(A.class, A.class)),
82                     nullValue());
83      }
84      
85      @Test
86      public void testAnyChainNonEmptyContextSuccess() throws Exception {
87          assertThat(ContextPattern.any().matches(makeContext(A.class)),
88                     notNullValue());
89          assertThat(ContextPattern.any().matches(makeContext(A.class, B.class)),
90                     notNullValue());
91          assertThat(ContextPattern.any().matches(makeContext(A.class, Ap.class)),
92                     notNullValue());
93          assertThat(ContextPattern.any().matches(makeContext(A.class, B.class, C.class)),
94                     notNullValue());
95      }
96      
97      @Test
98      public void testSubsequenceEqualChainContextSuccess() throws Exception {
99          // Test that a subsequence pattern matches
100         assertThat(ContextPattern.subsequence(A.class)
101                                  .matches(makeContext(A.class)),
102                    notNullValue());
103         assertThat(ContextPattern.subsequence(A.class, B.class)
104                                  .matches(makeContext(A.class, B.class)),
105                    notNullValue());
106         assertThat(ContextPattern.subsequence(A.class, B.class, C.class)
107                                  .matches(makeContext(A.class, B.class, C.class)),
108                    notNullValue());
109     }
110     
111     @Test
112     public void testSubstringChainSuccess() throws Exception {
113         assertThat(ContextPattern.subsequence(A.class)
114                                  .matches(makeContext(A.class, B.class, C.class)),
115                    notNullValue());
116         assertThat(ContextPattern.subsequence(B.class)
117                                  .matches(makeContext(A.class, B.class, C.class)),
118                    notNullValue());
119         assertThat(ContextPattern.subsequence(C.class)
120                                  .matches(makeContext(A.class, B.class, C.class)), notNullValue());
121 
122         assertThat(ContextPattern.subsequence(A.class, B.class)
123                                  .matches(makeContext(A.class, B.class, C.class)),
124                    notNullValue());
125         assertThat(ContextPattern.subsequence(B.class, C.class)
126                                  .matches(makeContext(A.class, B.class, C.class)), notNullValue());
127         
128         assertThat(ContextPattern.subsequence(B.class, C.class)
129                                  .matches(makeContext(A.class, B.class, C.class, Ap.class)),
130                    notNullValue());
131     }
132     
133     @Test
134     public void testSubsequenceChainSuccess() throws Exception {
135         assertThat(ContextPattern.subsequence(A.class, C.class)
136                                  .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)),
137                    notNullValue());
138         assertThat(ContextPattern.subsequence(A.class, Ap.class)
139                                  .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)),
140                    notNullValue());
141         assertThat(ContextPattern.subsequence(A.class, Bp.class)
142                                  .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)),
143                    notNullValue());
144         assertThat(ContextPattern.subsequence(A.class, C.class, Bp.class)
145                                  .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)),
146                    notNullValue());
147         assertThat(ContextPattern.subsequence(A.class, B.class, Ap.class)
148                                  .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)),
149                    notNullValue());
150         assertThat(ContextPattern.subsequence(B.class, Cp.class)
151                                  .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)),
152                    notNullValue());
153         assertThat(ContextPattern.subsequence(A.class, Cp.class)
154                                  .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)),
155                    notNullValue());
156         assertThat(ContextPattern.subsequence(C.class, Ap.class, Bp.class)
157                                  .matches(makeContext(A.class, B.class, C.class, Ap.class, Bp.class, Cp.class)),
158                    notNullValue());
159     }
160     
161     @Test
162     public void testMatcherInheritenceSuccess() throws Exception {
163         assertThat(ContextPattern.subsequence(A.class).matches(makeContext(Ap.class)), notNullValue());
164 
165         assertThat(ContextPattern.subsequence(A.class, C.class).matches(makeContext(Ap.class, Cp.class)), notNullValue());
166         assertThat(ContextPattern.subsequence(A.class, C.class).matches(makeContext(A.class, Cp.class)), notNullValue());
167         assertThat(ContextPattern.subsequence(A.class, C.class).matches(makeContext(Ap.class, C.class)), notNullValue());
168     }
169     
170     @Test
171     public void testNonSubsequenceFail() throws Exception {
172         assertThat(ContextPattern.subsequence(A.class).matches(makeContext(B.class)), nullValue());
173         assertThat(ContextPattern.subsequence(A.class, B.class).matches(makeContext(B.class, A.class)), nullValue());
174         assertThat(ContextPattern.subsequence(B.class, A.class, C.class).matches(makeContext(C.class, B.class, A.class)), nullValue());
175         assertThat(ContextPattern.subsequence(A.class, B.class, C.class).matches(makeContext(C.class, B.class, A.class)), nullValue());
176     }
177     
178     @Test
179     public void testSuperstringFail() throws Exception {
180         assertThat(ContextPattern.subsequence(A.class, B.class).matches(makeContext(A.class)), nullValue());
181         assertThat(ContextPattern.subsequence(A.class, B.class, C.class).matches(makeContext(A.class, C.class)), nullValue());
182         assertThat(ContextPattern.subsequence(A.class, B.class, C.class).matches(makeContext(A.class, B.class)), nullValue());
183     }
184 
185     @Test
186     public void testTailAnchoredMatch() {
187         ContextMatcher matcher = ContextPattern.any().append(A.class);
188         assertThat(matcher.matches(makeContext()),
189                    nullValue());
190         assertThat(matcher.matches(makeContext(A.class)),
191                    notNullValue());
192         assertThat(matcher.matches(makeContext(B.class, A.class)),
193                    notNullValue());
194         assertThat(matcher.matches(makeContext(A.class, B.class)),
195                    nullValue());
196     }
197 
198     @Test
199     public void testAnchoredAndUnanchored() {
200         ContextMatcher matcher = ContextPattern.any()
201                                                .append(A.class)
202                                                .append(B.class)
203                                                .appendDotStar();
204         assertThat(matcher.matches(makeContext()),
205                    nullValue());
206         assertThat(matcher.matches(makeContext(A.class, B.class)),
207                    notNullValue());
208         assertThat(matcher.matches(makeContext(A.class, B.class, C.class)),
209                    notNullValue());
210         assertThat(matcher.matches(makeContext(A.class, C.class, B.class)),
211                    nullValue());
212     }
213 
214     @Test
215     public void testOrderByCloseness() {
216         ContextPattern patA = ContextPattern.subsequence(A.class);
217         ContextPattern patB = ContextPattern.subsequence(B.class);
218         InjectionContext ctx1 = makeContext(A.class, B.class);
219         // B matches more closely than A
220         assertThat(patB.matches(ctx1), lessThan(patA.matches(ctx1)));
221         // A matches the same as itself
222         assertThat(patA.matches(ctx1),
223                    allOf(lessThanOrEqualTo(patA.matches(ctx1)),
224                          greaterThanOrEqualTo(patA.matches(ctx1))));
225     }
226 
227     @Test
228     public void testOrderByLength() {
229         ContextPattern patShort = ContextPattern.subsequence(B.class);
230         ContextPattern patLong = ContextPattern.subsequence(A.class, B.class);
231         InjectionContext ctx1 = makeContext(A.class, B.class);
232         // Long matches more closely than short
233         assertThat(patLong.matches(ctx1), lessThan(patShort.matches(ctx1)));
234         // Long matches like itself.
235         assertThat(patLong.matches(ctx1),
236                    allOf(lessThanOrEqualTo(patLong.matches(ctx1)),
237                          greaterThanOrEqualTo(patLong.matches(ctx1))));
238     }
239 
240     @Test
241     public void testOrderByLengthAfterCloseness() {
242         ContextPattern patClose = ContextPattern.subsequence(C.class);
243         ContextPattern patFar = ContextPattern.subsequence(A.class, B.class);
244         InjectionContext ctx1 = makeContext(A.class, B.class, C.class);
245         // Close matches more closely than long
246         assertThat(patClose.matches(ctx1), lessThan(patFar.matches(ctx1)));
247     }
248 
249     @Test
250     public void testOrderByType() {
251         ContextPattern patStrict = ContextPattern.subsequence(Ap.class);
252         ContextPattern patLoose = ContextPattern.subsequence(A.class);
253         InjectionContext ctx1 = makeContext(Ap.class);
254         // Tight matches more tightly
255         assertThat(patStrict.matches(ctx1),
256                    lessThan(patLoose.matches(ctx1)));
257     }
258 
259     @Test
260     public void testOrderByTypeLast() {
261         ContextPattern patStrict = ContextPattern.subsequence(Bp.class);
262         ContextPattern patLong = ContextPattern.subsequence(A.class, B.class);
263         InjectionContext ctx1 = makeContext(A.class, Bp.class);
264         // Length trumps tightness
265         assertThat(patLong.matches(ctx1),
266                    lessThan(patStrict.matches(ctx1)));
267     }
268 
269     @Test
270     public void testOrderSubsequenceBeforeOther() {
271         ContextPattern patAny = ContextPattern.any();
272         ContextPattern patLimited = ContextPattern.subsequence(B.class);
273         InjectionContext ctx1 = makeContext(A.class, B.class);
274         // B matches more closely than A
275         assertThat(patLimited.matches(ctx1), lessThan(patAny.matches(ctx1)));
276     }
277 
278     private InjectionContext makeContext(Class<?>... types) {
279         InjectionContext context = DependencySolver.initialContext();
280         for (Class<?> type: types) {
281             MockSatisfaction sat = new MockSatisfaction(type, new ArrayList<Desire>());
282             context = context.extend(sat, new MockInjectionPoint(type, null, false));
283         }
284         return context;
285     }
286     
287     private static class A {}
288     private static class B {}
289     private static class C {}
290     
291     private static class Ap extends A {}
292     private static class Bp extends B {}
293     private static class Cp extends C {}
294 }