PageRenderTime 35ms CodeModel.GetById 17ms app.highlight 15ms RepoModel.GetById 0ms app.codeStats 0ms

/extensions/multibindings/test/com/google/inject/multibindings/ProvidesIntoTest.java

https://gitlab.com/metamorphiccode/guice
Java | 373 lines | 298 code | 55 blank | 20 comment | 2 complexity | e40e15868bd759d67810995fe6d4460f MD5 | raw file
  1/**
  2 * Copyright (C) 2015 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.multibindings;
 18
 19import static com.google.inject.Asserts.assertContains;
 20import static com.google.inject.name.Names.named;
 21import static java.lang.annotation.RetentionPolicy.RUNTIME;
 22
 23import com.google.common.base.Optional;
 24import com.google.common.collect.ImmutableMap;
 25import com.google.common.collect.ImmutableSet;
 26import com.google.inject.AbstractModule;
 27import com.google.inject.CreationException;
 28import com.google.inject.Guice;
 29import com.google.inject.Injector;
 30import com.google.inject.Key;
 31import com.google.inject.Module;
 32import com.google.inject.multibindings.ProvidesIntoOptional.Type;
 33import com.google.inject.name.Named;
 34
 35import junit.framework.TestCase;
 36
 37import java.lang.annotation.Retention;
 38import java.lang.reflect.Field;
 39import java.util.Map;
 40import java.util.Set;
 41
 42/**
 43 * Tests the various @ProvidesInto annotations.
 44 *
 45 * @author sameb@google.com (Sam Berlin)
 46 */
 47public class ProvidesIntoTest extends TestCase {
 48
 49  public void testAnnotation() throws Exception {
 50    Injector injector = Guice.createInjector(MultibindingsScanner.asModule(), new AbstractModule() {
 51      @Override protected void configure() {}
 52
 53      @ProvidesIntoSet
 54      @Named("foo")
 55      String setFoo() { return "foo"; }
 56
 57      @ProvidesIntoSet
 58      @Named("foo")
 59      String setFoo2() { return "foo2"; }
 60
 61      @ProvidesIntoSet
 62      @Named("bar")
 63      String setBar() { return "bar"; }
 64
 65      @ProvidesIntoSet
 66      @Named("bar")
 67      String setBar2() { return "bar2"; }
 68
 69      @ProvidesIntoSet
 70      String setNoAnnotation() { return "na"; }
 71
 72      @ProvidesIntoSet
 73      String setNoAnnotation2() { return "na2"; }
 74
 75      @ProvidesIntoMap
 76      @StringMapKey("fooKey")
 77      @Named("foo")
 78      String mapFoo() { return "foo"; }
 79
 80      @ProvidesIntoMap
 81      @StringMapKey("foo2Key")
 82      @Named("foo")
 83      String mapFoo2() { return "foo2"; }
 84
 85      @ProvidesIntoMap
 86      @ClassMapKey(String.class)
 87      @Named("bar")
 88      String mapBar() { return "bar"; }
 89
 90      @ProvidesIntoMap
 91      @ClassMapKey(Number.class)
 92      @Named("bar")
 93      String mapBar2() { return "bar2"; }
 94
 95      @ProvidesIntoMap
 96      @TestEnumKey(TestEnum.A)
 97      String mapNoAnnotation() { return "na"; }
 98
 99      @ProvidesIntoMap
100      @TestEnumKey(TestEnum.B)
101      String mapNoAnnotation2() { return "na2"; }
102
103      @ProvidesIntoMap
104      @WrappedKey(number = 1)
105      Number wrapped1() { return 11; }
106
107      @ProvidesIntoMap
108      @WrappedKey(number = 2)
109      Number wrapped2() { return 22; }
110
111      @ProvidesIntoOptional(ProvidesIntoOptional.Type.DEFAULT)
112      @Named("foo")
113      String optionalDefaultFoo() { return "foo"; }
114
115      @ProvidesIntoOptional(ProvidesIntoOptional.Type.ACTUAL)
116      @Named("foo")
117      String optionalActualFoo() { return "foo2"; }
118
119      @ProvidesIntoOptional(ProvidesIntoOptional.Type.DEFAULT)
120      @Named("bar")
121      String optionalDefaultBar() { return "bar"; }
122
123      @ProvidesIntoOptional(ProvidesIntoOptional.Type.ACTUAL)
124      String optionalActualBar() { return "na2"; }
125    });
126
127    Set<String> fooSet = injector.getInstance(new Key<Set<String>>(named("foo")) {});
128    assertEquals(ImmutableSet.of("foo", "foo2"), fooSet);
129
130    Set<String> barSet = injector.getInstance(new Key<Set<String>>(named("bar")) {});
131    assertEquals(ImmutableSet.of("bar", "bar2"), barSet);
132
133    Set<String> noAnnotationSet = injector.getInstance(new Key<Set<String>>() {});
134    assertEquals(ImmutableSet.of("na", "na2"), noAnnotationSet);
135
136    Map<String, String> fooMap =
137        injector.getInstance(new Key<Map<String, String>>(named("foo")) {});
138    assertEquals(ImmutableMap.of("fooKey", "foo", "foo2Key", "foo2"), fooMap);
139
140    Map<Class<?>, String> barMap =
141        injector.getInstance(new Key<Map<Class<?>, String>>(named("bar")) {});
142    assertEquals(ImmutableMap.of(String.class, "bar", Number.class, "bar2"), barMap);
143
144    Map<TestEnum, String> noAnnotationMap =
145        injector.getInstance(new Key<Map<TestEnum, String>>() {});
146    assertEquals(ImmutableMap.of(TestEnum.A, "na", TestEnum.B, "na2"), noAnnotationMap);
147
148    Map<WrappedKey, Number> wrappedMap =
149        injector.getInstance(new Key<Map<WrappedKey, Number>>() {});
150    assertEquals(ImmutableMap.of(wrappedKeyFor(1), 11, wrappedKeyFor(2), 22), wrappedMap);
151
152    Optional<String> fooOptional =
153        injector.getInstance(new Key<Optional<String>>(named("foo")) {});
154    assertEquals("foo2", fooOptional.get());
155
156    Optional<String> barOptional =
157        injector.getInstance(new Key<Optional<String>>(named("bar")) {});
158    assertEquals("bar", barOptional.get());
159
160    Optional<String> noAnnotationOptional =
161        injector.getInstance(new Key<Optional<String>>() {});
162    assertEquals("na2", noAnnotationOptional.get());
163  }
164
165  enum TestEnum {
166    A, B
167  }
168
169  @MapKey(unwrapValue = true)
170  @Retention(RUNTIME)
171  @interface TestEnumKey {
172    TestEnum value();
173  }
174
175  @MapKey(unwrapValue = false)
176  @Retention(RUNTIME)
177  @interface WrappedKey {
178    int number();
179  }
180  
181  @SuppressWarnings("unused") @WrappedKey(number=1) private static Object wrappedKey1Holder;
182  @SuppressWarnings("unused") @WrappedKey(number=2) private static Object wrappedKey2Holder;
183  WrappedKey wrappedKeyFor(int number) throws Exception {
184    Field field;
185    switch (number) {
186      case 1:
187        field = ProvidesIntoTest.class.getDeclaredField("wrappedKey1Holder");
188        break;
189      case 2:
190        field = ProvidesIntoTest.class.getDeclaredField("wrappedKey2Holder");
191        break;
192      default:
193        throw new IllegalArgumentException("only 1 or 2 supported");
194    }
195    return field.getAnnotation(WrappedKey.class);
196  }
197  
198  public void testDoubleScannerIsIgnored() {
199    Injector injector = Guice.createInjector(
200        MultibindingsScanner.asModule(),
201        MultibindingsScanner.asModule(),
202        new AbstractModule() {
203          @Override protected void configure() {}
204          @ProvidesIntoSet String provideFoo() { return "foo"; }
205        }
206    );
207    assertEquals(ImmutableSet.of("foo"), injector.getInstance(new Key<Set<String>>() {}));
208  }
209  
210  @MapKey(unwrapValue = true)
211  @Retention(RUNTIME)
212  @interface ArrayUnwrappedKey {
213    int[] value();
214  }
215  
216  public void testArrayKeys_unwrapValuesTrue() {
217    Module m = new AbstractModule() {
218      @Override protected void configure() {}
219      @ProvidesIntoMap @ArrayUnwrappedKey({1, 2}) String provideFoo() { return "foo"; }
220    };
221    try {
222      Guice.createInjector(MultibindingsScanner.asModule(), m);
223      fail();
224    } catch (CreationException ce) {
225      assertEquals(1, ce.getErrorMessages().size());
226      assertContains(ce.getMessage(),
227          "Array types are not allowed in a MapKey with unwrapValue=true: "
228              + ArrayUnwrappedKey.class.getName(),
229          "at " + m.getClass().getName() + ".provideFoo(");
230    }    
231  }
232
233  @MapKey(unwrapValue = false)
234  @Retention(RUNTIME)
235  @interface ArrayWrappedKey {
236    int[] number();
237  }
238  
239  @SuppressWarnings("unused") @ArrayWrappedKey(number={1, 2}) private static Object arrayWrappedKeyHolder12;
240  @SuppressWarnings("unused") @ArrayWrappedKey(number={3, 4}) private static Object arrayWrappedKeyHolder34;
241  ArrayWrappedKey arrayWrappedKeyFor(int number) throws Exception {
242    Field field;
243    switch (number) {
244      case 12:
245        field = ProvidesIntoTest.class.getDeclaredField("arrayWrappedKeyHolder12");
246        break;
247      case 34:
248        field = ProvidesIntoTest.class.getDeclaredField("arrayWrappedKeyHolder34");
249        break;
250      default:
251        throw new IllegalArgumentException("only 1 or 2 supported");
252    }
253    return field.getAnnotation(ArrayWrappedKey.class);
254  }
255  
256  public void testArrayKeys_unwrapValuesFalse() throws Exception {
257    Module m = new AbstractModule() {
258      @Override protected void configure() {}
259      @ProvidesIntoMap @ArrayWrappedKey(number = {1, 2}) String provideFoo() { return "foo"; }
260      @ProvidesIntoMap @ArrayWrappedKey(number = {3, 4}) String provideBar() { return "bar"; }
261    };
262    Injector injector = Guice.createInjector(MultibindingsScanner.asModule(), m);
263    Map<ArrayWrappedKey, String> map =
264        injector.getInstance(new Key<Map<ArrayWrappedKey, String>>() {});
265    ArrayWrappedKey key12 = arrayWrappedKeyFor(12);
266    ArrayWrappedKey key34 = arrayWrappedKeyFor(34);
267    assertEquals("foo", map.get(key12));
268    assertEquals("bar", map.get(key34));
269    assertEquals(2, map.size());
270  }
271  
272  public void testProvidesIntoSetWithMapKey() {
273    Module m = new AbstractModule() {
274      @Override protected void configure() {}
275      @ProvidesIntoSet @TestEnumKey(TestEnum.A) String provideFoo() { return "foo"; }
276    };
277    try {
278      Guice.createInjector(MultibindingsScanner.asModule(), m);
279      fail();
280    } catch (CreationException ce) {
281      assertEquals(1, ce.getErrorMessages().size());
282      assertContains(ce.getMessage(), "Found a MapKey annotation on non map binding at "
283          + m.getClass().getName() + ".provideFoo");
284    }
285  }
286  
287  public void testProvidesIntoOptionalWithMapKey() {
288    Module m = new AbstractModule() {
289      @Override protected void configure() {}
290
291      @ProvidesIntoOptional(Type.ACTUAL)
292      @TestEnumKey(TestEnum.A)
293      String provideFoo() {
294        return "foo";
295      }
296    };
297    try {
298      Guice.createInjector(MultibindingsScanner.asModule(), m);
299      fail();
300    } catch (CreationException ce) {
301      assertEquals(1, ce.getErrorMessages().size());
302      assertContains(ce.getMessage(), "Found a MapKey annotation on non map binding at "
303          + m.getClass().getName() + ".provideFoo");
304    }
305  }
306  
307  public void testProvidesIntoMapWithoutMapKey() {
308    Module m = new AbstractModule() {
309      @Override protected void configure() {}
310      @ProvidesIntoMap String provideFoo() { return "foo"; }
311    };
312    try {
313      Guice.createInjector(MultibindingsScanner.asModule(), m);
314      fail();
315    } catch (CreationException ce) {
316      assertEquals(1, ce.getErrorMessages().size());
317      assertContains(ce.getMessage(), "No MapKey found for map binding at "
318          + m.getClass().getName() + ".provideFoo");
319    }
320  }
321  
322  @MapKey(unwrapValue = true)
323  @Retention(RUNTIME)
324  @interface TestEnumKey2 {
325    TestEnum value();
326  }
327  
328  public void testMoreThanOneMapKeyAnnotation() {
329    Module m = new AbstractModule() {
330      @Override protected void configure() {}
331
332      @ProvidesIntoMap
333      @TestEnumKey(TestEnum.A)
334      @TestEnumKey2(TestEnum.B)
335      String provideFoo() {
336        return "foo";
337      }
338    };
339    try {
340      Guice.createInjector(MultibindingsScanner.asModule(), m);
341      fail();
342    } catch (CreationException ce) {
343      assertEquals(1, ce.getErrorMessages().size());
344      assertContains(ce.getMessage(), "Found more than one MapKey annotations on "
345          + m.getClass().getName() + ".provideFoo");
346    }    
347  }
348  
349  @MapKey(unwrapValue = true)
350  @Retention(RUNTIME)
351  @interface MissingValueMethod {
352  }
353  
354  public void testMapKeyMissingValueMethod() {
355    Module m = new AbstractModule() {
356      @Override protected void configure() {}
357
358      @ProvidesIntoMap
359      @MissingValueMethod
360      String provideFoo() {
361        return "foo";
362      }
363    };
364    try {
365      Guice.createInjector(MultibindingsScanner.asModule(), m);
366      fail();
367    } catch (CreationException ce) {
368      assertEquals(1, ce.getErrorMessages().size());
369      assertContains(ce.getMessage(), "No 'value' method in MapKey with unwrapValue=true: "
370          + MissingValueMethod.class.getName());
371    }    
372  }
373}