Home | History | Annotate | Download | only in spi
      1 /**
      2  * Copyright (C) 2008 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.Asserts.getDeclaringSourcePart;
     21 import static com.google.inject.Asserts.isIncludeStackTraceComplete;
     22 
     23 import com.google.common.collect.ImmutableSet;
     24 import com.google.common.collect.Lists;
     25 import com.google.inject.AbstractModule;
     26 import com.google.inject.Binding;
     27 import com.google.inject.Guice;
     28 import com.google.inject.Inject;
     29 import com.google.inject.Injector;
     30 import com.google.inject.Key;
     31 import com.google.inject.Module;
     32 import com.google.inject.Provider;
     33 import com.google.inject.Scope;
     34 import com.google.inject.Scopes;
     35 import com.google.inject.Singleton;
     36 import com.google.inject.Stage;
     37 import com.google.inject.name.Names;
     38 
     39 import junit.framework.AssertionFailedError;
     40 import junit.framework.TestCase;
     41 
     42 import java.lang.reflect.Constructor;
     43 import java.util.Collections;
     44 import java.util.Comparator;
     45 import java.util.Iterator;
     46 import java.util.List;
     47 import java.util.concurrent.atomic.AtomicBoolean;
     48 import java.util.logging.Logger;
     49 
     50 /**
     51  * @author jessewilson (at) google.com (Jesse Wilson)
     52  */
     53 public class SpiBindingsTest extends TestCase {
     54 
     55   public void testBindConstant() {
     56     checkInjector(
     57         new AbstractModule() {
     58           protected void configure() {
     59             bindConstant().annotatedWith(Names.named("one")).to(1);
     60           }
     61         },
     62 
     63         new FailingElementVisitor() {
     64           @Override public <T> Void visit(Binding<T> binding) {
     65             assertTrue(binding instanceof InstanceBinding);
     66             assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
     67             return null;
     68           }
     69         }
     70     );
     71   }
     72 
     73   public void testToInstanceBinding() {
     74     checkInjector(
     75         new AbstractModule() {
     76           protected void configure() {
     77             bind(String.class).toInstance("A");
     78           }
     79         },
     80 
     81         new FailingElementVisitor() {
     82           @Override public <T> Void visit(Binding<T> binding) {
     83             assertTrue(binding instanceof InstanceBinding);
     84             checkBindingSource(binding);
     85             assertEquals(Key.get(String.class), binding.getKey());
     86             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
     87               @Override public Void visit(InstanceBinding<? extends T> binding) {
     88                 assertEquals("A", binding.getInstance());
     89                 return null;
     90               }
     91             });
     92             binding.acceptScopingVisitor(new FailingBindingScopingVisitor() {
     93               public Void visitEagerSingleton() {
     94                 return null;
     95               }
     96             });
     97             return null;
     98           }
     99         }
    100     );
    101   }
    102 
    103   public void testToProviderBinding() {
    104     final Provider<String> stringProvider = new StringProvider();
    105 
    106     checkInjector(
    107         new AbstractModule() {
    108           protected void configure() {
    109             bind(String.class).toProvider(stringProvider);
    110           }
    111         },
    112 
    113         new FailingElementVisitor() {
    114           @Override public <T> Void visit(Binding<T> binding) {
    115             assertTrue(binding instanceof ProviderInstanceBinding);
    116             checkBindingSource(binding);
    117             assertEquals(Key.get(String.class), binding.getKey());
    118             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    119               @Override public Void visit(
    120                   ProviderInstanceBinding<? extends T> binding) {
    121                 assertSame(stringProvider, binding.getUserSuppliedProvider());
    122                 return null;
    123               }
    124             });
    125             return null;
    126           }
    127         }
    128     );
    129   }
    130 
    131   public void testToProviderKeyBinding() {
    132     checkInjector(
    133         new AbstractModule() {
    134           protected void configure() {
    135             bind(String.class).toProvider(StringProvider.class);
    136           }
    137         },
    138 
    139         new FailingElementVisitor() {
    140           @Override public <T> Void visit(Binding<T> binding) {
    141             assertTrue(binding instanceof ProviderKeyBinding);
    142             checkBindingSource(binding);
    143             assertEquals(Key.get(String.class), binding.getKey());
    144             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    145               @Override public Void visit(ProviderKeyBinding<? extends T> binding) {
    146                 assertEquals(Key.get(StringProvider.class), binding.getProviderKey());
    147                 return null;
    148               }
    149             });
    150             return null;
    151           }
    152         }
    153     );
    154   }
    155 
    156   public void testToKeyBinding() {
    157     final Key<String> aKey = Key.get(String.class, Names.named("a"));
    158     final Key<String> bKey = Key.get(String.class, Names.named("b"));
    159 
    160     checkInjector(
    161         new AbstractModule() {
    162           protected void configure() {
    163             bind(aKey).to(bKey);
    164             bind(bKey).toInstance("B");
    165           }
    166         },
    167 
    168         new FailingElementVisitor() {
    169           @Override public <T> Void visit(Binding<T> binding) {
    170             assertTrue(binding instanceof LinkedKeyBinding);
    171             checkBindingSource(binding);
    172             assertEquals(aKey, binding.getKey());
    173             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    174               @Override public Void visit(LinkedKeyBinding<? extends T> binding) {
    175                 assertEquals(bKey, binding.getLinkedKey());
    176                 return null;
    177               }
    178             });
    179             return null;
    180           }
    181         },
    182 
    183         new FailingElementVisitor() {
    184           @Override public <T> Void visit(Binding<T> binding) {
    185             assertEquals(bKey, binding.getKey());
    186             return null;
    187           }
    188         }
    189     );
    190   }
    191 
    192   public void testToConstructorBinding() {
    193     checkInjector(
    194         new AbstractModule() {
    195           protected void configure() {
    196             bind(D.class);
    197           }
    198         },
    199 
    200         new FailingElementVisitor() {
    201           @Override public <T> Void visit(Binding<T> binding) {
    202             assertTrue(binding instanceof ConstructorBinding);
    203             checkBindingSource(binding);
    204             assertEquals(Key.get(D.class), binding.getKey());
    205             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    206               @Override public Void visit(ConstructorBinding<? extends T> binding) {
    207                 Constructor<?> expected = D.class.getDeclaredConstructors()[0];
    208                 assertEquals(expected, binding.getConstructor().getMember());
    209                 assertEquals(ImmutableSet.<InjectionPoint>of(), binding.getInjectableMembers());
    210                 return null;
    211               }
    212             });
    213             return null;
    214           }
    215         }
    216     );
    217   }
    218 
    219   public void testConstantBinding() {
    220     checkInjector(
    221         new AbstractModule() {
    222           protected void configure() {
    223             bindConstant().annotatedWith(Names.named("one")).to(1);
    224           }
    225         },
    226 
    227         new FailingElementVisitor() {
    228           @Override public <T> Void visit(Binding<T> binding) {
    229             assertTrue(binding instanceof InstanceBinding);
    230             checkBindingSource(binding);
    231             assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
    232             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    233               @Override public Void visit(InstanceBinding<? extends T> binding) {
    234                 assertEquals(1, binding.getInstance());
    235                 return null;
    236               }
    237             });
    238             return null;
    239           }
    240         }
    241     );
    242   }
    243 
    244   public void testConvertedConstantBinding() {
    245     Injector injector = Guice.createInjector(new AbstractModule() {
    246       protected void configure() {
    247         bindConstant().annotatedWith(Names.named("one")).to("1");
    248       }
    249     });
    250 
    251     Binding<Integer> binding = injector.getBinding(Key.get(Integer.class, Names.named("one")));
    252     assertEquals(Key.get(Integer.class, Names.named("one")), binding.getKey());
    253     checkBindingSource(binding);
    254     assertTrue(binding instanceof ConvertedConstantBinding);
    255     binding.acceptTargetVisitor(new FailingTargetVisitor<Integer>() {
    256       @Override public Void visit(
    257           ConvertedConstantBinding<? extends Integer> binding) {
    258         assertEquals((Integer) 1, binding.getValue());
    259         assertEquals(Key.get(String.class, Names.named("one")), binding.getSourceKey());
    260         return null;
    261       }
    262     });
    263   }
    264 
    265   public void testProviderBinding() {
    266     Injector injector = Guice.createInjector(new AbstractModule() {
    267       protected void configure() {
    268         bind(String.class).toInstance("A");
    269       }
    270     });
    271 
    272     Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {};
    273     Binding<Provider<String>> binding = injector.getBinding(providerOfStringKey);
    274     assertEquals(providerOfStringKey, binding.getKey());
    275     checkBindingSource(binding);
    276     assertTrue(binding instanceof ProviderBinding);
    277     binding.acceptTargetVisitor(new FailingTargetVisitor<Provider<String>>() {
    278       @Override public Void visit(
    279           ProviderBinding<? extends Provider<String>> binding) {
    280         assertEquals(Key.get(String.class), binding.getProvidedKey());
    281         return null;
    282       }
    283     });
    284   }
    285 
    286   public void testScopes() {
    287     checkInjector(
    288         new AbstractModule() {
    289           protected void configure() {
    290             bind(String.class).annotatedWith(Names.named("a"))
    291                 .toProvider(StringProvider.class).in(Singleton.class);
    292             bind(String.class).annotatedWith(Names.named("b"))
    293                 .toProvider(StringProvider.class).in(Scopes.SINGLETON);
    294             bind(String.class).annotatedWith(Names.named("c"))
    295                 .toProvider(StringProvider.class).asEagerSingleton();
    296             bind(String.class).annotatedWith(Names.named("d"))
    297                 .toProvider(StringProvider.class);
    298           }
    299         },
    300 
    301         new FailingElementVisitor() {
    302           @Override public <T> Void visit(Binding<T> command) {
    303             assertEquals(Key.get(String.class, Names.named("a")), command.getKey());
    304             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
    305               @Override public Void visitScope(Scope scope) {
    306                 // even though we bound with an annotation, the injector always uses instances
    307                 assertSame(Scopes.SINGLETON, scope);
    308                 return null;
    309               }
    310             });
    311             return null;
    312           }
    313         },
    314 
    315         new FailingElementVisitor() {
    316           @Override public <T> Void visit(Binding<T> command) {
    317             assertEquals(Key.get(String.class, Names.named("b")), command.getKey());
    318             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
    319               @Override public Void visitScope(Scope scope) {
    320                 assertSame(Scopes.SINGLETON, scope);
    321                 return null;
    322               }
    323             });
    324             return null;
    325           }
    326         },
    327 
    328         new FailingElementVisitor() {
    329           @Override public <T> Void visit(Binding<T> command) {
    330             assertEquals(Key.get(String.class, Names.named("c")), command.getKey());
    331             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
    332               @Override public Void visitEagerSingleton() {
    333                 return null;
    334               }
    335             });
    336             return null;
    337           }
    338         },
    339 
    340         new FailingElementVisitor() {
    341           @Override public <T> Void visit(Binding<T> command) {
    342             assertEquals(Key.get(String.class, Names.named("d")), command.getKey());
    343             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
    344               @Override public Void visitNoScoping() {
    345                 return null;
    346               }
    347             });
    348             return null;
    349           }
    350         }
    351     );
    352   }
    353 
    354   public void testExtensionSpi() {
    355     final AtomicBoolean visiting = new AtomicBoolean(false);
    356 
    357     final Injector injector = Guice.createInjector(new AbstractModule() {
    358       protected void configure() {
    359         bind(String.class).toProvider(new ProviderWithExtensionVisitor<String>() {
    360           public <B, V> V acceptExtensionVisitor(BindingTargetVisitor<B, V> visitor,
    361               ProviderInstanceBinding<? extends B> binding) {
    362             assertSame(this, binding.getUserSuppliedProvider());
    363             // We can't always check for FailingSpiTargetVisitor,
    364             // because constructing the injector visits here, and we need
    365             // to process the binding as normal
    366             if(visiting.get()) {
    367               assertTrue("visitor: " + visitor, visitor instanceof FailingSpiTargetVisitor);
    368               return (V)"visited";
    369             } else {
    370               return visitor.visit(binding);
    371             }
    372           }
    373 
    374           public String get() {
    375             return "FooBar";
    376           }
    377         });
    378       }
    379     });
    380 
    381     visiting.set(true);
    382 
    383     // Check for Provider<String> binding -- that is still a ProviderBinding.
    384     Key<Provider<String>> providerOfStringKey = new Key<Provider<String>>() {};
    385     Binding<Provider<String>> providerBinding = injector.getBinding(providerOfStringKey);
    386     assertEquals(providerOfStringKey, providerBinding.getKey());
    387     checkBindingSource(providerBinding);
    388     assertTrue("binding: " + providerBinding, providerBinding instanceof ProviderBinding);
    389     providerBinding.acceptTargetVisitor(new FailingTargetVisitor<Provider<String>>() {
    390       @Override public Void visit(ProviderBinding<? extends Provider<String>> binding) {
    391         assertEquals(Key.get(String.class), binding.getProvidedKey());
    392         return null;
    393       }
    394     });
    395 
    396     // Check for String binding -- that one is ProviderInstanceBinding, and gets hooked
    397     Binding<String> binding = injector.getBinding(String.class);
    398     assertEquals(Key.get(String.class), binding.getKey());
    399     checkBindingSource(binding);
    400     assertTrue(binding instanceof ProviderInstanceBinding);
    401     assertEquals("visited", binding.acceptTargetVisitor(new FailingSpiTargetVisitor<String>()));
    402   }
    403 
    404   private static class FailingSpiTargetVisitor<T> extends DefaultBindingTargetVisitor<T, String> {
    405     @Override
    406     protected String visitOther(Binding<? extends T> binding) {
    407       throw new AssertionFailedError();
    408     }
    409   }
    410 
    411   public void checkBindingSource(Binding binding) {
    412     assertContains(binding.getSource().toString(), getDeclaringSourcePart(getClass()));
    413     ElementSource source = (ElementSource) binding.getSource();
    414     assertFalse(source.getModuleClassNames().isEmpty());
    415     if (isIncludeStackTraceComplete()) {
    416       assertTrue(source.getStackTrace().length > 0);
    417     } else {
    418       assertEquals(0, source.getStackTrace().length);
    419     }
    420   }
    421 
    422   public void checkInjector(Module module, ElementVisitor<?>... visitors) {
    423     Injector injector = Guice.createInjector(module);
    424 
    425     List<Binding<?>> bindings = Lists.newArrayList(injector.getBindings().values());
    426     for (Iterator<Binding<?>> i = bindings.iterator(); i.hasNext(); ) {
    427       if (BUILT_IN_BINDINGS.contains(i.next().getKey())) {
    428         i.remove();
    429       }
    430     }
    431 
    432     Collections.sort(bindings, orderByKey);
    433 
    434     assertEquals(bindings.size(), visitors.length);
    435 
    436     for (int i = 0; i < visitors.length; i++) {
    437       ElementVisitor<?> visitor = visitors[i];
    438       Binding<?> binding = bindings.get(i);
    439       binding.acceptVisitor(visitor);
    440     }
    441   }
    442 
    443   private final ImmutableSet<Key<?>> BUILT_IN_BINDINGS = ImmutableSet.of(
    444       Key.get(Injector.class), Key.get(Stage.class), Key.get(Logger.class));
    445 
    446   private final Comparator<Binding<?>> orderByKey = new Comparator<Binding<?>>() {
    447     public int compare(Binding<?> a, Binding<?> b) {
    448       return a.getKey().toString().compareTo(b.getKey().toString());
    449     }
    450   };
    451 
    452   private static class StringProvider implements Provider<String> {
    453     public String get() {
    454       return "A";
    455     }
    456   }
    457 
    458   private static class C { }
    459 
    460   private static class D extends C {
    461     @Inject public D(Injector unused) { }
    462   }
    463 }
    464