Home | History | Annotate | Download | only in spi
      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 
     17 package com.google.inject.spi;
     18 
     19 import static com.google.inject.Asserts.assertContains;
     20 import static com.google.inject.name.Names.named;
     21 import static java.lang.annotation.ElementType.METHOD;
     22 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     23 
     24 import com.google.common.collect.ImmutableSet;
     25 import com.google.common.collect.Iterables;
     26 import com.google.inject.AbstractModule;
     27 import com.google.inject.Binder;
     28 import com.google.inject.Binding;
     29 import com.google.inject.CreationException;
     30 import com.google.inject.Exposed;
     31 import com.google.inject.Guice;
     32 import com.google.inject.Injector;
     33 import com.google.inject.Key;
     34 import com.google.inject.Module;
     35 import com.google.inject.PrivateModule;
     36 import com.google.inject.internal.util.StackTraceElements;
     37 import com.google.inject.name.Named;
     38 import com.google.inject.name.Names;
     39 
     40 import junit.framework.TestCase;
     41 
     42 import java.lang.annotation.Annotation;
     43 import java.lang.annotation.Documented;
     44 import java.lang.annotation.Retention;
     45 import java.lang.annotation.Target;
     46 import java.util.Set;
     47 
     48 /** Tests for {@link ModuleAnnotatedMethodScanner} usage. */
     49 public class ModuleAnnotatedMethodScannerTest extends TestCase {
     50 
     51   public void testScanning() throws Exception {
     52     Module module = new AbstractModule() {
     53       @Override protected void configure() {}
     54 
     55       @TestProvides @Named("foo") String foo() {
     56         return "foo";
     57       }
     58 
     59       @TestProvides @Named("foo2") String foo2() {
     60         return "foo2";
     61       }
     62     };
     63     Injector injector = Guice.createInjector(module, NamedMunger.module());
     64 
     65     // assert no bindings named "foo" or "foo2" exist -- they were munged.
     66     assertMungedBinding(injector, String.class, "foo", "foo");
     67     assertMungedBinding(injector, String.class, "foo2", "foo2");
     68 
     69     Binding<String> fooBinding = injector.getBinding(Key.get(String.class, named("foo-munged")));
     70     Binding<String> foo2Binding = injector.getBinding(Key.get(String.class, named("foo2-munged")));
     71     // Validate the provider has a sane toString
     72     assertEquals(methodName(TestProvides.class, "foo", module),
     73         fooBinding.getProvider().toString());
     74     assertEquals(methodName(TestProvides.class, "foo2", module),
     75         foo2Binding.getProvider().toString());
     76   }
     77 
     78   public void testSkipSources() throws Exception {
     79     Module module = new AbstractModule() {
     80       @Override protected void configure() {
     81         binder().skipSources(getClass()).install(new AbstractModule() {
     82           @Override protected void configure() {}
     83 
     84           @TestProvides @Named("foo") String foo() { return "foo"; }
     85         });
     86       }
     87     };
     88     Injector injector = Guice.createInjector(module, NamedMunger.module());
     89     assertMungedBinding(injector, String.class, "foo", "foo");
     90   }
     91 
     92   public void testWithSource() throws Exception {
     93     Module module = new AbstractModule() {
     94       @Override protected void configure() {
     95         binder().withSource("source").install(new AbstractModule() {
     96           @Override protected void configure() {}
     97 
     98           @TestProvides @Named("foo") String foo() { return "foo"; }
     99         });
    100       }
    101     };
    102     Injector injector = Guice.createInjector(module, NamedMunger.module());
    103     assertMungedBinding(injector, String.class, "foo", "foo");
    104   }
    105 
    106   public void testMoreThanOneClaimedAnnotationFails() throws Exception {
    107     Module module = new AbstractModule() {
    108       @Override protected void configure() {}
    109 
    110       @TestProvides @TestProvides2 String foo() {
    111         return "foo";
    112       }
    113     };
    114     try {
    115       Guice.createInjector(module, NamedMunger.module());
    116       fail();
    117     } catch(CreationException expected) {
    118       assertEquals(1, expected.getErrorMessages().size());
    119       assertContains(expected.getMessage(),
    120           "More than one annotation claimed by NamedMunger on method "
    121               + module.getClass().getName() + ".foo(). Methods can only have "
    122               + "one annotation claimed per scanner.");
    123     }
    124   }
    125 
    126   private String methodName(Class<? extends Annotation> annotation, String method, Object container)
    127       throws Exception {
    128     return "@" + annotation.getName() + " "
    129         + StackTraceElements.forMember(container.getClass().getDeclaredMethod(method));
    130   }
    131 
    132   @Documented @Target(METHOD) @Retention(RUNTIME)
    133   private @interface TestProvides {}
    134 
    135   @Documented @Target(METHOD) @Retention(RUNTIME)
    136   private @interface TestProvides2 {}
    137 
    138   private static class NamedMunger extends ModuleAnnotatedMethodScanner {
    139     static Module module() {
    140       return new AbstractModule() {
    141         @Override protected void configure() {
    142           binder().scanModulesForAnnotatedMethods(new NamedMunger());
    143         }
    144       };
    145     }
    146 
    147     @Override
    148     public String toString() {
    149       return "NamedMunger";
    150     }
    151 
    152     @Override
    153     public Set<? extends Class<? extends Annotation>> annotationClasses() {
    154       return ImmutableSet.of(TestProvides.class, TestProvides2.class);
    155     }
    156 
    157     @Override
    158     public <T> Key<T> prepareMethod(Binder binder, Annotation annotation, Key<T> key,
    159         InjectionPoint injectionPoint) {
    160       return Key.get(key.getTypeLiteral(),
    161           Names.named(((Named) key.getAnnotation()).value() + "-munged"));
    162     }
    163   }
    164 
    165   private void assertMungedBinding(Injector injector, Class<?> clazz, String originalName,
    166       Object expectedValue) {
    167     assertNull(injector.getExistingBinding(Key.get(clazz, named(originalName))));
    168     Binding<?> fooBinding = injector.getBinding(Key.get(clazz, named(originalName + "-munged")));
    169     assertEquals(expectedValue, fooBinding.getProvider().get());
    170   }
    171 
    172   public void testFailingScanner() {
    173     try {
    174       Guice.createInjector(new SomeModule(), FailingScanner.module());
    175       fail();
    176     } catch (CreationException expected) {
    177       Message m = Iterables.getOnlyElement(expected.getErrorMessages());
    178       assertEquals(
    179           "An exception was caught and reported. Message: Failing in the scanner.",
    180           m.getMessage());
    181       assertEquals(IllegalStateException.class, m.getCause().getClass());
    182       ElementSource source = (ElementSource) Iterables.getOnlyElement(m.getSources());
    183       assertEquals(SomeModule.class.getName(),
    184           Iterables.getOnlyElement(source.getModuleClassNames()));
    185       assertEquals(String.class.getName() + " " + SomeModule.class.getName() + ".aString()",
    186           source.toString());
    187     }
    188   }
    189 
    190   public static class FailingScanner extends ModuleAnnotatedMethodScanner {
    191     static Module module() {
    192       return new AbstractModule() {
    193         @Override protected void configure() {
    194           binder().scanModulesForAnnotatedMethods(new FailingScanner());
    195         }
    196       };
    197     }
    198 
    199     @Override public Set<? extends Class<? extends Annotation>> annotationClasses() {
    200       return ImmutableSet.of(TestProvides.class);
    201     }
    202 
    203     @Override public <T> Key<T> prepareMethod(
    204         Binder binder, Annotation rawAnnotation, Key<T> key, InjectionPoint injectionPoint) {
    205       throw new IllegalStateException("Failing in the scanner.");
    206     }
    207   }
    208 
    209   static class SomeModule extends AbstractModule {
    210     @TestProvides String aString() {
    211       return "Foo";
    212     }
    213 
    214     @Override protected void configure() {}
    215   }
    216 
    217   public void testChildInjectorInheritsScanner() {
    218     Injector parent = Guice.createInjector(NamedMunger.module());
    219     Injector child = parent.createChildInjector(new AbstractModule() {
    220       @Override protected void configure() {}
    221 
    222       @TestProvides @Named("foo") String foo() {
    223         return "foo";
    224       }
    225     });
    226     assertMungedBinding(child, String.class, "foo", "foo");
    227   }
    228 
    229   public void testChildInjectorScannersDontImpactSiblings() {
    230     Module module = new AbstractModule() {
    231       @Override
    232       protected void configure() {}
    233 
    234       @TestProvides @Named("foo") String foo() {
    235         return "foo";
    236       }
    237     };
    238     Injector parent = Guice.createInjector();
    239     Injector child = parent.createChildInjector(NamedMunger.module(), module);
    240     assertMungedBinding(child, String.class, "foo", "foo");
    241 
    242     // no foo nor foo-munged in sibling, since scanner never saw it.
    243     Injector sibling = parent.createChildInjector(module);
    244     assertNull(sibling.getExistingBinding(Key.get(String.class, named("foo"))));
    245     assertNull(sibling.getExistingBinding(Key.get(String.class, named("foo-munged"))));
    246   }
    247 
    248   public void testPrivateModuleInheritScanner_usingPrivateModule() {
    249     Injector injector = Guice.createInjector(NamedMunger.module(), new PrivateModule() {
    250       @Override protected void configure() {}
    251 
    252       @Exposed @TestProvides @Named("foo") String foo() {
    253         return "foo";
    254       }
    255     });
    256     assertMungedBinding(injector, String.class, "foo", "foo");
    257   }
    258 
    259   public void testPrivateModule_skipSourcesWithinPrivateModule() {
    260     Injector injector = Guice.createInjector(NamedMunger.module(), new PrivateModule() {
    261       @Override protected void configure() {
    262         binder().skipSources(getClass()).install(new AbstractModule() {
    263           @Override protected void configure() {}
    264           @Exposed @TestProvides @Named("foo") String foo() {
    265             return "foo";
    266           }
    267         });
    268       }
    269     });
    270     assertMungedBinding(injector, String.class, "foo", "foo");
    271   }
    272 
    273   public void testPrivateModule_skipSourcesForPrivateModule() {
    274     Injector injector = Guice.createInjector(NamedMunger.module(), new AbstractModule() {
    275       @Override protected void configure() {
    276         binder().skipSources(getClass()).install(new PrivateModule() {
    277           @Override protected void configure() {}
    278 
    279           @Exposed @TestProvides @Named("foo") String foo() {
    280             return "foo";
    281           }
    282         });
    283       }});
    284     assertMungedBinding(injector, String.class, "foo", "foo");
    285   }
    286 
    287   public void testPrivateModuleInheritScanner_usingPrivateBinder() {
    288     Injector injector = Guice.createInjector(NamedMunger.module(), new AbstractModule() {
    289       @Override protected void configure() {
    290         binder().newPrivateBinder().install(new AbstractModule() {
    291           @Override protected void configure() {}
    292 
    293           @Exposed @TestProvides @Named("foo") String foo() {
    294             return "foo";
    295           }
    296         });
    297       }
    298     });
    299     assertMungedBinding(injector, String.class, "foo", "foo");
    300   }
    301 
    302   public void testPrivateModuleInheritScanner_skipSourcesFromPrivateBinder() {
    303     Injector injector = Guice.createInjector(NamedMunger.module(), new AbstractModule() {
    304       @Override protected void configure() {
    305         binder().newPrivateBinder().skipSources(getClass()).install(new AbstractModule() {
    306           @Override protected void configure() {}
    307 
    308           @Exposed @TestProvides @Named("foo") String foo() {
    309             return "foo";
    310           }
    311         });
    312       }
    313     });
    314     assertMungedBinding(injector, String.class, "foo", "foo");
    315   }
    316 
    317   public void testPrivateModuleInheritScanner_skipSourcesFromPrivateBinder2() {
    318     Injector injector = Guice.createInjector(NamedMunger.module(), new AbstractModule() {
    319       @Override protected void configure() {
    320         binder().skipSources(getClass()).newPrivateBinder().install(new AbstractModule() {
    321           @Override protected void configure() {}
    322 
    323           @Exposed @TestProvides @Named("foo") String foo() {
    324             return "foo";
    325           }
    326         });
    327       }
    328     });
    329     assertMungedBinding(injector, String.class, "foo", "foo");
    330   }
    331 
    332   public void testPrivateModuleScannersDontImpactSiblings_usingPrivateModule() {
    333     Injector injector = Guice.createInjector(new PrivateModule() {
    334       @Override protected void configure() {
    335         install(NamedMunger.module());
    336       }
    337 
    338       @Exposed @TestProvides @Named("foo") String foo() {
    339         return "foo";
    340       }
    341     }, new PrivateModule() {
    342       @Override protected void configure() {}
    343 
    344       // ignored! (because the scanner doesn't run over this module)
    345       @Exposed @TestProvides @Named("foo") String foo() {
    346         return "foo";
    347       }
    348     });
    349     assertMungedBinding(injector, String.class, "foo", "foo");
    350   }
    351 
    352   public void testPrivateModuleScannersDontImpactSiblings_usingPrivateBinder() {
    353     Injector injector = Guice.createInjector(new AbstractModule() {
    354       @Override protected void configure() {
    355         binder().newPrivateBinder().install(new AbstractModule() {
    356           @Override protected void configure() {
    357             install(NamedMunger.module());
    358           }
    359 
    360           @Exposed @TestProvides @Named("foo") String foo() {
    361             return "foo";
    362           }
    363         });
    364       }
    365     }, new AbstractModule() {
    366       @Override protected void configure() {
    367         binder().newPrivateBinder().install(new AbstractModule() {
    368           @Override protected void configure() {}
    369 
    370           // ignored! (because the scanner doesn't run over this module)
    371           @Exposed @TestProvides @Named("foo") String foo() {
    372             return "foo";
    373           }
    374         });
    375       }});
    376     assertMungedBinding(injector, String.class, "foo", "foo");
    377   }
    378 
    379   public void testPrivateModuleWithinPrivateModule() {
    380     Injector injector = Guice.createInjector(NamedMunger.module(), new PrivateModule() {
    381       @Override protected void configure() {
    382         expose(Key.get(String.class, named("foo-munged")));
    383         install(new PrivateModule() {
    384           @Override protected void configure() {}
    385 
    386           @Exposed @TestProvides @Named("foo") String foo() {
    387             return "foo";
    388           }
    389         });
    390       }
    391     });
    392     assertMungedBinding(injector, String.class, "foo", "foo");
    393   }
    394 }
    395