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.common.collect.Iterables.getOnlyElement;
     20 import static com.google.inject.Asserts.assertContains;
     21 import static com.google.inject.Asserts.getDeclaringSourcePart;
     22 import static com.google.inject.Asserts.isIncludeStackTraceComplete;
     23 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     24 
     25 import com.google.common.collect.ImmutableMap;
     26 import com.google.common.collect.ImmutableSet;
     27 import com.google.inject.AbstractModule;
     28 import com.google.inject.Binding;
     29 import com.google.inject.BindingAnnotation;
     30 import com.google.inject.Inject;
     31 import com.google.inject.Key;
     32 import com.google.inject.MembersInjector;
     33 import com.google.inject.Module;
     34 import com.google.inject.PrivateBinder;
     35 import com.google.inject.Provider;
     36 import com.google.inject.Scope;
     37 import com.google.inject.Scopes;
     38 import com.google.inject.Singleton;
     39 import com.google.inject.Stage;
     40 import com.google.inject.TypeLiteral;
     41 import com.google.inject.binder.AnnotatedBindingBuilder;
     42 import com.google.inject.binder.AnnotatedConstantBindingBuilder;
     43 import com.google.inject.binder.ConstantBindingBuilder;
     44 import com.google.inject.binder.ScopedBindingBuilder;
     45 import com.google.inject.matcher.Matcher;
     46 import com.google.inject.matcher.Matchers;
     47 import com.google.inject.name.Named;
     48 import com.google.inject.name.Names;
     49 import com.google.inject.util.Providers;
     50 
     51 import junit.framework.TestCase;
     52 
     53 import java.lang.annotation.Annotation;
     54 import java.lang.annotation.ElementType;
     55 import java.lang.annotation.Retention;
     56 import java.lang.annotation.Target;
     57 import java.lang.reflect.Constructor;
     58 import java.lang.reflect.Field;
     59 import java.util.ArrayList;
     60 import java.util.Arrays;
     61 import java.util.Collection;
     62 import java.util.HashMap;
     63 import java.util.Iterator;
     64 import java.util.List;
     65 import java.util.Map;
     66 import java.util.Set;
     67 import java.util.TreeSet;
     68 import java.util.concurrent.atomic.AtomicInteger;
     69 import java.util.concurrent.atomic.AtomicReference;
     70 
     71 /**
     72  * @author jessewilson (at) google.com (Jesse Wilson)
     73  */
     74 public class ElementsTest extends TestCase {
     75 
     76   // Binder fidelity tests
     77 
     78   public void testAddMessageErrorCommand() {
     79     checkModule(
     80         new AbstractModule() {
     81           @Override protected void configure() {
     82             addError("Message %s %d %s", "A", 5, "C");
     83           }
     84         },
     85 
     86         new FailingElementVisitor() {
     87           @Override public Void visit(Message command) {
     88             assertEquals("Message A 5 C", command.getMessage());
     89             assertNull(command.getCause());
     90             assertContains(command.getSources().toString(),
     91                 ElementsTest.class.getName(),
     92                 getDeclaringSourcePart(ElementsTest.class));
     93             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
     94             return null;
     95           }
     96         }
     97     );
     98   }
     99 
    100   public void testAddThrowableErrorCommand() {
    101     checkModule(
    102         new AbstractModule() {
    103           protected void configure() {
    104             addError(new Exception("A"));
    105           }
    106         },
    107 
    108         new FailingElementVisitor() {
    109           @Override public Void visit(Message command) {
    110             assertEquals("A", command.getCause().getMessage());
    111             assertEquals(command.getMessage(),
    112                 "An exception was caught and reported. Message: A");
    113             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
    114             return null;
    115           }
    116         }
    117     );
    118   }
    119 
    120   public void testErrorsAddedWhenExceptionsAreThrown() {
    121     checkModule(
    122         new AbstractModule() {
    123           protected void configure() {
    124             install(new AbstractModule() {
    125               protected void configure() {
    126                 throw new RuntimeException("Throwing RuntimeException in AbstractModule.configure().");
    127               }
    128             });
    129 
    130             addError("Code after the exception still gets executed");
    131           }
    132         },
    133 
    134         new FailingElementVisitor() {
    135           @Override public Void visit(Message command) {
    136             assertEquals("Throwing RuntimeException in AbstractModule.configure().",
    137                 command.getCause().getMessage());
    138             return null;
    139           }
    140         },
    141 
    142         new FailingElementVisitor() {
    143           @Override public Void visit(Message command) {
    144             assertEquals("Code after the exception still gets executed",
    145                 command.getMessage());
    146             return null;
    147           }
    148         }
    149     );
    150   }
    151 
    152   private <T> T getInstance(Binding<T> binding) {
    153     return binding.acceptTargetVisitor(Elements.<T>getInstanceVisitor());
    154   }
    155 
    156   public void testBindConstantAnnotations() {
    157     checkModule(
    158         new AbstractModule() {
    159           protected void configure() {
    160             bindConstant().annotatedWith(SampleAnnotation.class).to("A");
    161             bindConstant().annotatedWith(Names.named("Bee")).to("B");
    162           }
    163         },
    164 
    165         new FailingElementVisitor() {
    166           @Override public <T> Void visit(Binding<T> command) {
    167             assertTrue(command instanceof InstanceBinding);
    168             assertEquals(Key.get(String.class, SampleAnnotation.class), command.getKey());
    169             assertEquals("A", getInstance(command));
    170             return null;
    171           }
    172         },
    173 
    174         new FailingElementVisitor() {
    175           @Override public <T> Void visit(Binding<T> command) {
    176             assertTrue(command instanceof InstanceBinding);
    177             assertEquals(Key.get(String.class, Names.named("Bee")), command.getKey());
    178             assertEquals("B", getInstance(command));
    179             return null;
    180           }
    181         }
    182     );
    183   }
    184 
    185   public void testBindConstantTypes() {
    186     checkModule(
    187         new AbstractModule() {
    188           protected void configure() {
    189             bindConstant().annotatedWith(Names.named("String")).to("A");
    190             bindConstant().annotatedWith(Names.named("int")).to(2);
    191             bindConstant().annotatedWith(Names.named("long")).to(3L);
    192             bindConstant().annotatedWith(Names.named("boolean")).to(false);
    193             bindConstant().annotatedWith(Names.named("double")).to(5.0d);
    194             bindConstant().annotatedWith(Names.named("float")).to(6.0f);
    195             bindConstant().annotatedWith(Names.named("short")).to((short) 7);
    196             bindConstant().annotatedWith(Names.named("char")).to('h');
    197             bindConstant().annotatedWith(Names.named("byte")).to((byte) 8);
    198             bindConstant().annotatedWith(Names.named("Class")).to(Iterator.class);
    199             bindConstant().annotatedWith(Names.named("Enum")).to(CoinSide.TAILS);
    200           }
    201         },
    202 
    203         new FailingElementVisitor() {
    204           @Override public <T> Void visit(Binding<T> command) {
    205             assertTrue(command instanceof InstanceBinding);
    206             assertEquals(Key.get(String.class, Names.named("String")), command.getKey());
    207             assertEquals("A", getInstance(command));
    208             return null;
    209           }
    210         },
    211 
    212         new FailingElementVisitor() {
    213           @Override public <T> Void visit(Binding<T> command) {
    214             assertTrue(command instanceof InstanceBinding);
    215             assertEquals(Key.get(Integer.class, Names.named("int")), command.getKey());
    216             assertEquals(2, getInstance(command));
    217             return null;
    218           }
    219         },
    220 
    221         new FailingElementVisitor() {
    222           @Override public <T> Void visit(Binding<T> command) {
    223             assertTrue(command instanceof InstanceBinding);
    224             assertEquals(Key.get(Long.class, Names.named("long")), command.getKey());
    225             assertEquals(3L, getInstance(command));
    226             return null;
    227           }
    228         },
    229 
    230         new FailingElementVisitor() {
    231           @Override public <T> Void visit(Binding<T> command) {
    232             assertTrue(command instanceof InstanceBinding);
    233             assertEquals(Key.get(Boolean.class, Names.named("boolean")), command.getKey());
    234             assertEquals(false, getInstance(command));
    235             return null;
    236           }
    237         },
    238 
    239         new FailingElementVisitor() {
    240           @Override public <T> Void visit(Binding<T> command) {
    241             assertTrue(command instanceof InstanceBinding);
    242             assertEquals(Key.get(Double.class, Names.named("double")), command.getKey());
    243             assertEquals(5.0d, getInstance(command));
    244             return null;
    245           }
    246         },
    247 
    248         new FailingElementVisitor() {
    249           @Override public <T> Void visit(Binding<T> command) {
    250             assertTrue(command instanceof InstanceBinding);
    251             assertEquals(Key.get(Float.class, Names.named("float")), command.getKey());
    252             assertEquals(6.0f, getInstance(command));
    253             return null;
    254           }
    255         },
    256 
    257         new FailingElementVisitor() {
    258           @Override public <T> Void visit(Binding<T> command) {
    259             assertTrue(command instanceof InstanceBinding);
    260             assertEquals(Key.get(Short.class, Names.named("short")), command.getKey());
    261             assertEquals((short) 7, getInstance(command));
    262             return null;
    263           }
    264         },
    265 
    266         new FailingElementVisitor() {
    267           @Override public <T> Void visit(Binding<T> command) {
    268             assertTrue(command instanceof InstanceBinding);
    269             assertEquals(Key.get(Character.class, Names.named("char")), command.getKey());
    270             assertEquals('h', getInstance(command));
    271             return null;
    272           }
    273         },
    274 
    275         new FailingElementVisitor() {
    276           @Override public <T> Void visit(Binding<T> command) {
    277             assertTrue(command instanceof InstanceBinding);
    278             assertEquals(Key.get(Byte.class, Names.named("byte")), command.getKey());
    279             assertEquals((byte) 8, getInstance(command));
    280             return null;
    281           }
    282         },
    283 
    284         new FailingElementVisitor() {
    285           @Override public <T> Void visit(Binding<T> command) {
    286             assertTrue(command instanceof InstanceBinding);
    287             assertEquals(Key.get(Class.class, Names.named("Class")), command.getKey());
    288             assertEquals(Iterator.class, getInstance(command));
    289             return null;
    290           }
    291         },
    292 
    293         new FailingElementVisitor() {
    294           @Override public <T> Void visit(Binding<T> command) {
    295             assertTrue(command instanceof InstanceBinding);
    296             assertEquals(Key.get(CoinSide.class, Names.named("Enum")), command.getKey());
    297             assertEquals(CoinSide.TAILS, getInstance(command));
    298             return null;
    299           }
    300         }
    301     );
    302   }
    303 
    304   public void testBindKeysNoAnnotations() {
    305     FailingElementVisitor keyChecker = new FailingElementVisitor() {
    306       @Override public <T> Void visit(Binding<T> command) {
    307         assertEquals(Key.get(String.class), command.getKey());
    308         return null;
    309       }
    310     };
    311 
    312     checkModule(
    313         new AbstractModule() {
    314           protected void configure() {
    315             bind(String.class).toInstance("A");
    316             bind(new TypeLiteral<String>() {}).toInstance("B");
    317             bind(Key.get(String.class)).toInstance("C");
    318           }
    319         },
    320         keyChecker,
    321         keyChecker,
    322         keyChecker
    323     );
    324   }
    325 
    326   public void testBindKeysWithAnnotationType() {
    327     FailingElementVisitor annotationChecker = new FailingElementVisitor() {
    328       @Override public <T> Void visit(Binding<T> command) {
    329         assertEquals(Key.get(String.class, SampleAnnotation.class), command.getKey());
    330         return null;
    331       }
    332     };
    333 
    334     checkModule(
    335         new AbstractModule() {
    336           protected void configure() {
    337             bind(String.class).annotatedWith(SampleAnnotation.class).toInstance("A");
    338             bind(new TypeLiteral<String>() {}).annotatedWith(SampleAnnotation.class).toInstance("B");
    339           }
    340         },
    341         annotationChecker,
    342         annotationChecker
    343     );
    344   }
    345 
    346   public void testBindKeysWithAnnotationInstance() {
    347     FailingElementVisitor annotationChecker = new FailingElementVisitor() {
    348       @Override public <T> Void visit(Binding<T> command) {
    349         assertEquals(Key.get(String.class, Names.named("a")), command.getKey());
    350         return null;
    351       }
    352     };
    353 
    354 
    355     checkModule(
    356         new AbstractModule() {
    357           protected void configure() {
    358             bind(String.class).annotatedWith(Names.named("a")).toInstance("B");
    359             bind(new TypeLiteral<String>() {}).annotatedWith(Names.named("a")).toInstance("C");
    360           }
    361         },
    362         annotationChecker,
    363         annotationChecker
    364     );
    365   }
    366 
    367   public void testBindToProvider() {
    368     final Provider<String> aProvider = new Provider<String>() {
    369       public String get() {
    370         return "A";
    371       }
    372     };
    373 
    374     final javax.inject.Provider<Integer> intJavaxProvider = new javax.inject.Provider<Integer>() {
    375       public Integer get() {
    376         return 42;
    377       }
    378     };
    379 
    380     final javax.inject.Provider<Double> doubleJavaxProvider = new javax.inject.Provider<Double>() {
    381       @javax.inject.Inject String string;
    382 
    383       public Double get() {
    384         return 42.42;
    385       }
    386     };
    387 
    388     checkModule(
    389         new AbstractModule() {
    390           protected void configure() {
    391             bind(String.class).toProvider(aProvider);
    392             bind(Integer.class).toProvider(intJavaxProvider);
    393             bind(Double.class).toProvider(doubleJavaxProvider);
    394             bind(List.class).toProvider(ListProvider.class);
    395             bind(Collection.class).toProvider(Key.get(ListProvider.class));
    396             bind(Iterable.class).toProvider(new TypeLiteral<TProvider<List>>() {});
    397           }
    398         },
    399 
    400         new FailingElementVisitor() {
    401           @Override public <T> Void visit(Binding<T> command) {
    402             assertTrue(command instanceof ProviderInstanceBinding);
    403             assertEquals(Key.get(String.class), command.getKey());
    404             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    405               @Override public Void visit(
    406                   ProviderInstanceBinding<? extends T> binding) {
    407                 assertSame(aProvider, binding.getUserSuppliedProvider());
    408                 assertSame(aProvider, binding.getProviderInstance());
    409                 return null;
    410               }
    411             });
    412             return null;
    413           }
    414         },
    415 
    416         new FailingElementVisitor() {
    417           @Override public <T> Void visit(Binding<T> command) {
    418             assertTrue(command instanceof ProviderInstanceBinding);
    419             assertEquals(Key.get(Integer.class), command.getKey());
    420             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    421               @Override public Void visit(
    422                   ProviderInstanceBinding<? extends T> binding) {
    423                 assertSame(intJavaxProvider, binding.getUserSuppliedProvider());
    424                 assertEquals(42, binding.getProviderInstance().get());
    425                 // we don't wrap this w/ dependencies if there were none.
    426                 assertFalse(binding.getProviderInstance() instanceof HasDependencies);
    427                 return null;
    428               }
    429             });
    430             return null;
    431           }
    432         },
    433 
    434         new FailingElementVisitor() {
    435           @Override public <T> Void visit(Binding<T> command) {
    436             assertTrue(command instanceof ProviderInstanceBinding);
    437             assertEquals(Key.get(Double.class), command.getKey());
    438             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    439               @Override public Void visit(
    440                   ProviderInstanceBinding<? extends T> binding) {
    441                 assertSame(doubleJavaxProvider, binding.getUserSuppliedProvider());
    442                 assertEquals(42.42, binding.getProviderInstance().get());
    443                 // we do wrap it with dependencies if there were some.
    444                 assertTrue(binding.getProviderInstance() instanceof HasDependencies);
    445                 Set<Dependency<?>> deps =
    446                     ((HasDependencies) binding.getProviderInstance()).getDependencies();
    447                 assertEquals(1, deps.size());
    448                 assertEquals(String.class,
    449                     deps.iterator().next().getKey().getTypeLiteral().getRawType());
    450                 return null;
    451               }
    452             });
    453             return null;
    454           }
    455         },
    456 
    457         new FailingElementVisitor() {
    458           @Override public <T> Void visit(Binding<T> command) {
    459             assertTrue(command instanceof ProviderKeyBinding);
    460             assertEquals(Key.get(List.class), command.getKey());
    461             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    462               @Override public Void visit(ProviderKeyBinding<? extends T> binding) {
    463                 assertEquals(Key.get(ListProvider.class), binding.getProviderKey());
    464                 return null;
    465               }
    466             });
    467             return null;
    468           }
    469         },
    470 
    471         new FailingElementVisitor() {
    472           @Override public <T> Void visit(Binding<T> command) {
    473             assertTrue(command instanceof ProviderKeyBinding);
    474             assertEquals(Key.get(Collection.class), command.getKey());
    475             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    476               @Override public Void visit(ProviderKeyBinding<? extends T> binding) {
    477                 assertEquals(Key.get(ListProvider.class), binding.getProviderKey());
    478                 return null;
    479               }
    480             });
    481             return null;
    482           }
    483         },
    484 
    485         new FailingElementVisitor() {
    486           @Override public <T> Void visit(Binding<T> command) {
    487             assertTrue(command instanceof ProviderKeyBinding);
    488             assertEquals(Key.get(Iterable.class), command.getKey());
    489             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    490               @Override public Void visit(ProviderKeyBinding<? extends T> binding) {
    491                 assertEquals(new Key<TProvider<List>>() {}, binding.getProviderKey());
    492                 return null;
    493               }
    494             });
    495             return null;
    496           }
    497         }
    498     );
    499   }
    500 
    501   public void testBindToLinkedBinding() {
    502     checkModule(
    503         new AbstractModule() {
    504           protected void configure() {
    505             bind(List.class).to(ArrayList.class);
    506             bind(Map.class).to(new TypeLiteral<HashMap<Integer, String>>() {});
    507             bind(Set.class).to(Key.get(TreeSet.class, SampleAnnotation.class));
    508           }
    509         },
    510 
    511         new FailingElementVisitor() {
    512           @Override public <T> Void visit(Binding<T> command) {
    513             assertTrue(command instanceof LinkedKeyBinding);
    514             assertEquals(Key.get(List.class), command.getKey());
    515             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    516               @Override public Void visit(LinkedKeyBinding<? extends T> binding) {
    517                 assertEquals(Key.get(ArrayList.class), binding.getLinkedKey());
    518                 return null;
    519               }
    520             });
    521             return null;
    522           }
    523         },
    524 
    525         new FailingElementVisitor() {
    526           @Override public <T> Void visit(Binding<T> command) {
    527             assertTrue(command instanceof LinkedKeyBinding);
    528             assertEquals(Key.get(Map.class), command.getKey());
    529             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    530               @Override public Void visit(LinkedKeyBinding<? extends T> binding) {
    531                 assertEquals(Key.get(new TypeLiteral<HashMap<Integer, String>>() {}),
    532                     binding.getLinkedKey());
    533                 return null;
    534               }
    535             });
    536             return null;
    537           }
    538         },
    539 
    540         new FailingElementVisitor() {
    541           @Override public <T> Void visit(Binding<T> command) {
    542             assertTrue(command instanceof LinkedKeyBinding);
    543             assertEquals(Key.get(Set.class), command.getKey());
    544             command.acceptTargetVisitor(new FailingTargetVisitor<T>() {
    545               @Override public Void visit(LinkedKeyBinding<? extends T> binding) {
    546                 assertEquals(Key.get(TreeSet.class, SampleAnnotation.class),
    547                     binding.getLinkedKey());
    548                 return null;
    549               }
    550             });
    551             return null;
    552           }
    553         }
    554     );
    555   }
    556 
    557   public void testBindToInstance() {
    558     checkModule(
    559         new AbstractModule() {
    560           protected void configure() {
    561             bind(String.class).toInstance("A");
    562           }
    563         },
    564 
    565         new FailingElementVisitor() {
    566           @Override public <T> Void visit(Binding<T> command) {
    567             assertTrue(command instanceof InstanceBinding);
    568             assertEquals(Key.get(String.class), command.getKey());
    569             assertEquals("A", getInstance(command));
    570             return null;
    571           }
    572         }
    573     );
    574   }
    575 
    576   public void testBindInScopes() {
    577     checkModule(
    578         new AbstractModule() {
    579           protected void configure() {
    580             bind(String.class);
    581             bind(List.class).to(ArrayList.class).in(Scopes.SINGLETON);
    582             bind(Map.class).to(HashMap.class).in(Singleton.class);
    583             bind(Set.class).to(TreeSet.class).asEagerSingleton();
    584           }
    585         },
    586 
    587         new FailingElementVisitor() {
    588           @Override public <T> Void visit(Binding<T> command) {
    589             assertEquals(Key.get(String.class), command.getKey());
    590             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
    591               @Override public Void visitNoScoping() {
    592                 return null;
    593               }
    594             });
    595             return null;
    596           }
    597         },
    598 
    599         new FailingElementVisitor() {
    600           @Override public <T> Void visit(Binding<T> command) {
    601             assertEquals(Key.get(List.class), command.getKey());
    602             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
    603               @Override public Void visitScope(Scope scope) {
    604                 assertEquals(Scopes.SINGLETON, scope);
    605                 return null;
    606               }
    607             });
    608             return null;
    609           }
    610         },
    611 
    612         new FailingElementVisitor() {
    613           @Override public <T> Void visit(Binding<T> command) {
    614             assertEquals(Key.get(Map.class), command.getKey());
    615             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
    616               @Override public Void visitScopeAnnotation(Class<? extends Annotation> annotation) {
    617                 assertEquals(Singleton.class, annotation);
    618                 return null;
    619               }
    620             });
    621             return null;
    622           }
    623         },
    624 
    625         new FailingElementVisitor() {
    626           @Override public <T> Void visit(Binding<T> command) {
    627             assertEquals(Key.get(Set.class), command.getKey());
    628             command.acceptScopingVisitor(new FailingBindingScopingVisitor() {
    629               public Void visitEagerSingleton() {
    630                 return null;
    631               }
    632             });
    633             return null;
    634           }
    635         }
    636     );
    637   }
    638 
    639   public void testBindToInstanceInScope() {
    640     checkModule(
    641         new AbstractModule() {
    642           protected void configure() {
    643             AnnotatedBindingBuilder<String> b = bind(String.class);
    644             b.toInstance("A");
    645             b.in(Singleton.class);
    646           }
    647         },
    648 
    649         new FailingElementVisitor() {
    650           @Override public <T> Void visit(Binding<T> command) {
    651             return null;
    652           }
    653         },
    654 
    655         new FailingElementVisitor() {
    656           @Override public Void visit(Message command) {
    657             assertEquals("Setting the scope is not permitted when binding to a single instance.",
    658                 command.getMessage());
    659             assertNull(command.getCause());
    660             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
    661             return null;
    662           }
    663         }
    664       );
    665   }
    666 
    667   public void testBindToInstanceScope() {
    668     checkModule(
    669         new AbstractModule() {
    670           protected void configure() {
    671             bind(String.class).toInstance("A");
    672           }
    673         },
    674 
    675         new FailingElementVisitor() {
    676           @Override public <T> Void visit(Binding<T> binding) {
    677             assertEquals(Key.get(String.class), binding.getKey());
    678             binding.acceptScopingVisitor(new FailingBindingScopingVisitor() {
    679               public Void visitEagerSingleton() {
    680                 return null;
    681               }
    682             });
    683             return null;
    684           }
    685         }
    686       );
    687   }
    688 
    689   /*if[AOP]*/
    690   public void testBindIntercepor() {
    691     final Matcher<Class> classMatcher = Matchers.subclassesOf(List.class);
    692     final Matcher<Object> methodMatcher = Matchers.any();
    693     final org.aopalliance.intercept.MethodInterceptor methodInterceptor
    694         = new org.aopalliance.intercept.MethodInterceptor() {
    695       public Object invoke(org.aopalliance.intercept.MethodInvocation methodInvocation) {
    696         return null;
    697       }
    698     };
    699 
    700     checkModule(
    701         new AbstractModule() {
    702           protected void configure() {
    703             bindInterceptor(classMatcher, methodMatcher, methodInterceptor);
    704           }
    705         },
    706 
    707         new FailingElementVisitor() {
    708           @Override public Void visit(InterceptorBinding command) {
    709             assertSame(classMatcher, command.getClassMatcher());
    710             assertSame(methodMatcher, command.getMethodMatcher());
    711             assertEquals(Arrays.asList(methodInterceptor), command.getInterceptors());
    712             return null;
    713           }
    714         }
    715     );
    716   }
    717   /*end[AOP]*/
    718 
    719   public void testBindScope() {
    720     checkModule(
    721         new AbstractModule() {
    722           protected void configure() {
    723             bindScope(SampleAnnotation.class, Scopes.NO_SCOPE);
    724           }
    725         },
    726 
    727         new FailingElementVisitor() {
    728           @Override public Void visit(ScopeBinding command) {
    729             assertSame(SampleAnnotation.class, command.getAnnotationType());
    730             assertSame(Scopes.NO_SCOPE, command.getScope());
    731             return null;
    732           }
    733         }
    734     );
    735   }
    736 
    737   public void testBindListener() {
    738     final Matcher<Object> typeMatcher = Matchers.only(TypeLiteral.get(String.class));
    739     final TypeListener listener = new TypeListener() {
    740       public <I> void hear(TypeLiteral<I> type, TypeEncounter<I> encounter) {
    741         throw new UnsupportedOperationException();
    742       }
    743     };
    744 
    745     checkModule(
    746         new AbstractModule() {
    747           protected void configure() {
    748             bindListener(typeMatcher, listener);
    749           }
    750         },
    751 
    752         new FailingElementVisitor() {
    753           @Override public Void visit(TypeListenerBinding binding) {
    754             assertSame(typeMatcher, binding.getTypeMatcher());
    755             assertSame(listener, binding.getListener());
    756             return null;
    757           }
    758         }
    759     );
    760   }
    761 
    762   public void testConvertToTypes() {
    763     final TypeConverter typeConverter = new TypeConverter() {
    764       public Object convert(String value, TypeLiteral<?> toType) {
    765         return value;
    766       }
    767     };
    768 
    769     checkModule(
    770         new AbstractModule() {
    771           protected void configure() {
    772             convertToTypes(Matchers.any(), typeConverter);
    773           }
    774         },
    775 
    776         new FailingElementVisitor() {
    777           @Override public Void visit(TypeConverterBinding command) {
    778             assertSame(typeConverter, command.getTypeConverter());
    779             assertSame(Matchers.any(), command.getTypeMatcher());
    780             return null;
    781           }
    782         }
    783     );
    784   }
    785 
    786   public void testGetProvider() {
    787     checkModule(
    788         new AbstractModule() {
    789           protected void configure() {
    790             Provider<String> keyGetProvider
    791                 = getProvider(Key.get(String.class, SampleAnnotation.class));
    792             try {
    793               keyGetProvider.get();
    794             } catch (IllegalStateException e) {
    795               assertEquals("This Provider cannot be used until the Injector has been created.",
    796                   e.getMessage());
    797             }
    798 
    799             Provider<String> typeGetProvider = getProvider(String.class);
    800             try {
    801               typeGetProvider.get();
    802             } catch (IllegalStateException e) {
    803               assertEquals("This Provider cannot be used until the Injector has been created.",
    804                   e.getMessage());
    805             }
    806           }
    807         },
    808 
    809         new FailingElementVisitor() {
    810           @Override public <T> Void visit(ProviderLookup<T> command) {
    811             assertEquals(Key.get(String.class, SampleAnnotation.class), command.getKey());
    812             assertNull(command.getDelegate());
    813             return null;
    814           }
    815         },
    816 
    817         new FailingElementVisitor() {
    818           @Override public <T> Void visit(ProviderLookup<T> command) {
    819             assertEquals(Key.get(String.class), command.getKey());
    820             assertNull(command.getDelegate());
    821             return null;
    822           }
    823         }
    824     );
    825   }
    826 
    827   public void testElementInitialization() {
    828     final AtomicReference<Provider<String>> providerFromBinder
    829         = new AtomicReference<Provider<String>>();
    830     final AtomicReference<MembersInjector<String>> membersInjectorFromBinder
    831         = new AtomicReference<MembersInjector<String>>();
    832 
    833     final AtomicReference<String> lastInjected = new AtomicReference<String>();
    834     final MembersInjector<String> stringInjector = new MembersInjector<String>() {
    835       public void injectMembers(String instance) {
    836         lastInjected.set(instance);
    837       }
    838     };
    839 
    840     checkModule(
    841         new AbstractModule() {
    842           protected void configure() {
    843             providerFromBinder.set(getProvider(String.class));
    844             membersInjectorFromBinder.set(getMembersInjector(String.class));
    845           }
    846         },
    847 
    848         new FailingElementVisitor() {
    849           public <T> Void visit(ProviderLookup<T> providerLookup) {
    850             @SuppressWarnings("unchecked") // we know that T is a String here
    851             ProviderLookup<String> stringLookup = (ProviderLookup<String>) providerLookup;
    852             stringLookup.initializeDelegate(Providers.of("out"));
    853 
    854             assertEquals("out", providerFromBinder.get().get());
    855             return null;
    856           }
    857         },
    858 
    859         new FailingElementVisitor() {
    860           @Override public <T> Void visit(MembersInjectorLookup<T> lookup) {
    861             @SuppressWarnings("unchecked") // we know that T is a String here
    862             MembersInjectorLookup<String> stringLookup = (MembersInjectorLookup<String>) lookup;
    863             stringLookup.initializeDelegate(stringInjector);
    864 
    865             membersInjectorFromBinder.get().injectMembers("in");
    866             assertEquals("in", lastInjected.get());
    867             return null;
    868           }
    869         });
    870   }
    871 
    872   public void testGetMembersInjector() {
    873     checkModule(
    874         new AbstractModule() {
    875           protected void configure() {
    876             MembersInjector<A<String>> typeMembersInjector
    877                 = getMembersInjector(new TypeLiteral<A<String>>() {});
    878             try {
    879               typeMembersInjector.injectMembers(new A<String>());
    880             } catch (IllegalStateException e) {
    881               assertEquals(
    882                   "This MembersInjector cannot be used until the Injector has been created.",
    883                   e.getMessage());
    884             }
    885 
    886             MembersInjector<String> classMembersInjector = getMembersInjector(String.class);
    887             try {
    888               classMembersInjector.injectMembers("hello");
    889             } catch (IllegalStateException e) {
    890               assertEquals(
    891                   "This MembersInjector cannot be used until the Injector has been created.",
    892                   e.getMessage());
    893             }
    894           }
    895         },
    896 
    897         new FailingElementVisitor() {
    898           @Override public <T> Void visit(MembersInjectorLookup<T> command) {
    899             assertEquals(new TypeLiteral<A<String>>() {}, command.getType());
    900             assertNull(command.getDelegate());
    901             return null;
    902           }
    903         },
    904 
    905         new FailingElementVisitor() {
    906           @Override public <T> Void visit(MembersInjectorLookup<T> command) {
    907             assertEquals(TypeLiteral.get(String.class), command.getType());
    908             assertNull(command.getDelegate());
    909             return null;
    910           }
    911         }
    912     );
    913   }
    914 
    915   public void testRequestInjection() {
    916     final Object firstObject = new Object();
    917     final Object secondObject = new Object();
    918 
    919     checkModule(
    920         new AbstractModule() {
    921           protected void configure() {
    922             requestInjection(firstObject);
    923             requestInjection(secondObject);
    924           }
    925         },
    926 
    927         new FailingElementVisitor() {
    928           @Override public Void visit(InjectionRequest<?> command) {
    929             assertEquals(firstObject, command.getInstance());
    930             return null;
    931           }
    932         },
    933 
    934         new FailingElementVisitor() {
    935           @Override public Void visit(InjectionRequest<?> command) {
    936             assertEquals(secondObject, command.getInstance());
    937             return null;
    938           }
    939         }
    940     );
    941   }
    942 
    943   public void testRequestStaticInjection() {
    944     checkModule(
    945         new AbstractModule() {
    946           protected void configure() {
    947             requestStaticInjection(ArrayList.class);
    948           }
    949         },
    950 
    951         new FailingElementVisitor() {
    952           @Override public Void visit(StaticInjectionRequest command) {
    953             assertEquals(ArrayList.class, command.getType());
    954             return null;
    955           }
    956         }
    957     );
    958   }
    959 
    960   public void testNewPrivateBinder() {
    961     final Key<Collection> collection = Key.get(Collection.class, SampleAnnotation.class);
    962     final Key<ArrayList> arrayList = Key.get(ArrayList.class);
    963     final ImmutableSet<Key<?>> collections = ImmutableSet.<Key<?>>of(arrayList, collection);
    964 
    965     final Key<?> a = Key.get(String.class, Names.named("a"));
    966     final Key<?> b = Key.get(String.class, Names.named("b"));
    967     final ImmutableSet<Key<?>> ab = ImmutableSet.of(a, b);
    968 
    969     checkModule(
    970         new AbstractModule() {
    971           protected void configure() {
    972             PrivateBinder one = binder().newPrivateBinder();
    973             one.expose(ArrayList.class);
    974             one.expose(Collection.class).annotatedWith(SampleAnnotation.class);
    975             one.bind(List.class).to(ArrayList.class);
    976 
    977             PrivateBinder two = binder().withSource("1 FooBar")
    978                 .newPrivateBinder().withSource("2 FooBar");
    979             two.expose(String.class).annotatedWith(Names.named("a"));
    980             two.expose(b);
    981             two.bind(List.class).to(ArrayList.class);
    982           }
    983         },
    984 
    985         new FailingElementVisitor() {
    986           @Override public Void visit(PrivateElements one) {
    987             assertEquals(collections, one.getExposedKeys());
    988             checkElements(one.getElements(),
    989                 new FailingElementVisitor() {
    990                   @Override public <T> Void visit(Binding<T> binding) {
    991                     assertEquals(Key.get(List.class), binding.getKey());
    992                     return null;
    993                   }
    994                 }
    995             );
    996             return null;
    997           }
    998         },
    999 
   1000         new ExternalFailureVisitor() {
   1001           @Override public Void visit(PrivateElements two) {
   1002             assertEquals(ab, two.getExposedKeys());
   1003             assertEquals("1 FooBar", two.getSource().toString());
   1004             checkElements(two.getElements(),
   1005                 new ExternalFailureVisitor() {
   1006                   @Override public <T> Void visit(Binding<T> binding) {
   1007                     assertEquals("2 FooBar", binding.getSource().toString());
   1008                     assertEquals(Key.get(List.class), binding.getKey());
   1009                     return null;
   1010                   }
   1011                 }
   1012             );
   1013             return null;
   1014           }
   1015         }
   1016     );
   1017   }
   1018 
   1019   public void testBindWithMultipleAnnotationsAddsError() {
   1020     checkModule(
   1021         new AbstractModule() {
   1022           protected void configure() {
   1023             AnnotatedBindingBuilder<String> abb = bind(String.class);
   1024             abb.annotatedWith(SampleAnnotation.class);
   1025             abb.annotatedWith(Names.named("A"));
   1026           }
   1027         },
   1028 
   1029         new FailingElementVisitor() {
   1030           @Override public <T> Void visit(Binding<T> command) {
   1031             return null;
   1032           }
   1033         },
   1034 
   1035         new FailingElementVisitor() {
   1036           @Override public Void visit(Message command) {
   1037             assertEquals("More than one annotation is specified for this binding.",
   1038                 command.getMessage());
   1039             assertNull(command.getCause());
   1040             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
   1041             return null;
   1042           }
   1043         }
   1044     );
   1045   }
   1046 
   1047   public void testBindWithMultipleTargetsAddsError() {
   1048     checkModule(
   1049         new AbstractModule() {
   1050           protected void configure() {
   1051             AnnotatedBindingBuilder<String> abb = bind(String.class);
   1052             abb.toInstance("A");
   1053             abb.toInstance("B");
   1054           }
   1055         },
   1056 
   1057         new FailingElementVisitor() {
   1058           @Override public <T> Void visit(Binding<T> command) {
   1059             return null;
   1060           }
   1061         },
   1062 
   1063         new FailingElementVisitor() {
   1064           @Override public Void visit(Message command) {
   1065             assertEquals("Implementation is set more than once.", command.getMessage());
   1066             assertNull(command.getCause());
   1067             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
   1068             return null;
   1069           }
   1070         }
   1071     );
   1072   }
   1073 
   1074   public void testBindWithMultipleScopesAddsError() {
   1075     checkModule(
   1076         new AbstractModule() {
   1077           protected void configure() {
   1078             ScopedBindingBuilder sbb = bind(List.class).to(ArrayList.class);
   1079             sbb.in(Scopes.NO_SCOPE);
   1080             sbb.asEagerSingleton();
   1081           }
   1082         },
   1083 
   1084         new FailingElementVisitor() {
   1085           @Override public <T> Void visit(Binding<T> command) {
   1086             return null;
   1087           }
   1088         },
   1089 
   1090         new FailingElementVisitor() {
   1091           @Override public Void visit(Message command) {
   1092             assertEquals("Scope is set more than once.", command.getMessage());
   1093             assertNull(command.getCause());
   1094             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
   1095             return null;
   1096           }
   1097         }
   1098     );
   1099   }
   1100 
   1101   public void testBindConstantWithMultipleAnnotationsAddsError() {
   1102     checkModule(
   1103         new AbstractModule() {
   1104           protected void configure() {
   1105             AnnotatedConstantBindingBuilder cbb = bindConstant();
   1106             cbb.annotatedWith(SampleAnnotation.class).to("A");
   1107             cbb.annotatedWith(Names.named("A"));
   1108           }
   1109         },
   1110 
   1111         new FailingElementVisitor() {
   1112           @Override public <T> Void visit(Binding<T> command) {
   1113             return null;
   1114           }
   1115         },
   1116 
   1117         new FailingElementVisitor() {
   1118           @Override public Void visit(Message command) {
   1119             assertEquals("More than one annotation is specified for this binding.",
   1120                 command.getMessage());
   1121             assertNull(command.getCause());
   1122             assertContains(command.getSource(), getDeclaringSourcePart(ElementsTest.class));
   1123             return null;
   1124           }
   1125         }
   1126     );
   1127   }
   1128 
   1129   public void testBindConstantWithMultipleTargetsAddsError() {
   1130     checkModule(
   1131         new AbstractModule() {
   1132           protected void configure() {
   1133             ConstantBindingBuilder cbb = bindConstant().annotatedWith(SampleAnnotation.class);
   1134             cbb.to("A");
   1135             cbb.to("B");
   1136           }
   1137         },
   1138 
   1139         new FailingElementVisitor() {
   1140           @Override public <T> Void visit(Binding<T> command) {
   1141             return null;
   1142           }
   1143         },
   1144 
   1145         new FailingElementVisitor() {
   1146           @Override public Void visit(Message message) {
   1147             assertEquals("Constant value is set more than once.", message.getMessage());
   1148             assertNull(message.getCause());
   1149             assertContains(message.getSource(), getDeclaringSourcePart(ElementsTest.class));
   1150             return null;
   1151           }
   1152         }
   1153     );
   1154   }
   1155 
   1156   public void testBindToConstructor() throws NoSuchMethodException, NoSuchFieldException {
   1157     final Constructor<A> aConstructor = A.class.getDeclaredConstructor();
   1158     final Constructor<B> bConstructor = B.class.getDeclaredConstructor(Object.class);
   1159     final Field field = B.class.getDeclaredField("stage");
   1160 
   1161     checkModule(
   1162         new AbstractModule() {
   1163           protected void configure() {
   1164             bind(A.class).toConstructor(aConstructor);
   1165             bind(B.class).toConstructor(bConstructor, new TypeLiteral<B<Integer>>() {})
   1166                 .in(Singleton.class);
   1167           }
   1168         },
   1169 
   1170         new FailingElementVisitor() {
   1171           @Override public <T> Void visit(Binding<T> binding) {
   1172             assertEquals(new Key<A>() {}, binding.getKey());
   1173 
   1174             return binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
   1175               @Override public Void visit(ConstructorBinding<? extends T> constructorBinding) {
   1176                 InjectionPoint injectionPoint = constructorBinding.getConstructor();
   1177                 assertEquals(aConstructor, injectionPoint.getMember());
   1178                 assertEquals(new TypeLiteral<A>() {}, injectionPoint.getDeclaringType());
   1179                 return null;
   1180               }
   1181             });
   1182           }
   1183         },
   1184 
   1185         new FailingElementVisitor() {
   1186           @Override public <T> Void visit(Binding<T> binding) {
   1187             assertEquals(new Key<B>() {}, binding.getKey());
   1188             binding.acceptScopingVisitor(new FailingBindingScopingVisitor() {
   1189               @Override public Void visitScopeAnnotation(Class<? extends Annotation> annotation) {
   1190                 assertEquals(Singleton.class, annotation);
   1191                 return null;
   1192               }
   1193             });
   1194 
   1195             binding.acceptTargetVisitor(new FailingTargetVisitor<T>() {
   1196               @Override public Void visit(ConstructorBinding<? extends T> constructorBinding) {
   1197                 assertEquals(bConstructor, constructorBinding.getConstructor().getMember());
   1198                 assertEquals(Key.get(Integer.class),
   1199                     getOnlyElement(constructorBinding.getConstructor().getDependencies()).getKey());
   1200                 assertEquals(field,
   1201                     getOnlyElement(constructorBinding.getInjectableMembers()).getMember());
   1202                 assertEquals(2, constructorBinding.getDependencies().size());
   1203 /*if[AOP]*/
   1204                 assertEquals(ImmutableMap.of(), constructorBinding.getMethodInterceptors());
   1205 /*end[AOP]*/
   1206                 return null;
   1207               }
   1208             });
   1209             return null;
   1210           }
   1211         }
   1212     );
   1213   }
   1214 
   1215   public void testBindToMalformedConstructor() throws NoSuchMethodException, NoSuchFieldException {
   1216     final Constructor<C> constructor = C.class.getDeclaredConstructor(Integer.class);
   1217 
   1218     checkModule(
   1219         new AbstractModule() {
   1220           protected void configure() {
   1221             bind(C.class).toConstructor(constructor);
   1222           }
   1223         },
   1224 
   1225         new FailingElementVisitor() {
   1226           @Override public <T> Void visit(Binding<T> binding) {
   1227             assertEquals(Key.get(C.class), binding.getKey());
   1228             assertTrue(binding instanceof UntargettedBinding);
   1229             return null;
   1230           }
   1231         },
   1232 
   1233         new ExternalFailureVisitor() {
   1234           @Override public Void visit(Message message) {
   1235             assertContains(message.getMessage(),
   1236                 C.class.getName() + ".a has more than one annotation ",
   1237                 Named.class.getName(), SampleAnnotation.class.getName());
   1238             return null;
   1239           }
   1240         },
   1241 
   1242         new ExternalFailureVisitor() {
   1243           @Override public Void visit(Message message) {
   1244             assertContains(message.getMessage(),
   1245                 C.class.getName() + ".<init>() has more than one annotation ",
   1246                 Named.class.getName(), SampleAnnotation.class.getName());
   1247             return null;
   1248           }
   1249         }
   1250     );
   1251   }
   1252 
   1253   // Business logic tests
   1254 
   1255   public void testModulesAreInstalledAtMostOnce() {
   1256     final AtomicInteger aConfigureCount = new AtomicInteger(0);
   1257     final Module a = new AbstractModule() {
   1258       public void configure() {
   1259         aConfigureCount.incrementAndGet();
   1260       }
   1261     };
   1262 
   1263     Elements.getElements(a, a);
   1264     assertEquals(1, aConfigureCount.get());
   1265 
   1266     aConfigureCount.set(0);
   1267     Module b = new AbstractModule() {
   1268       protected void configure() {
   1269         install(a);
   1270         install(a);
   1271       }
   1272     };
   1273 
   1274     Elements.getElements(b);
   1275     assertEquals(1, aConfigureCount.get());
   1276   }
   1277 
   1278   /**
   1279    * Ensures the module performs the commands consistent with {@code visitors}.
   1280    */
   1281   protected void checkModule(Module module, ElementVisitor<?>... visitors) {
   1282     List<Element> elements = Elements.getElements(module);
   1283     assertEquals(elements.size(), visitors.length);
   1284     checkElements(elements, visitors);
   1285   }
   1286 
   1287   protected void checkElements(List<Element> elements, ElementVisitor<?>... visitors) {
   1288     for (int i = 0; i < visitors.length; i++) {
   1289       ElementVisitor<?> visitor = visitors[i];
   1290       Element element = elements.get(i);
   1291       if (!(element instanceof Message)) {
   1292           ElementSource source = (ElementSource) element.getSource();
   1293           assertFalse(source.getModuleClassNames().isEmpty());
   1294           if (isIncludeStackTraceComplete()) {
   1295             assertTrue(source.getStackTrace().length > 0);
   1296           } else {
   1297             assertEquals(0, source.getStackTrace().length);
   1298           }
   1299       }
   1300       if (!(visitor instanceof ExternalFailureVisitor)) {
   1301         assertContains(element.getSource().toString(), getDeclaringSourcePart(ElementsTest.class));
   1302       }
   1303       element.acceptVisitor(visitor);
   1304     }
   1305   }
   1306 
   1307   private static class ListProvider implements Provider<List> {
   1308     public List get() {
   1309       return new ArrayList();
   1310     }
   1311   }
   1312 
   1313   private static class TProvider<T> implements Provider<T> {
   1314     public T get() {
   1315       return null;
   1316     }
   1317   }
   1318 
   1319   /**
   1320    * By extending this interface rather than FailingElementVisitor, the source of the error doesn't
   1321    * need to contain the string {@code ElementsTest.java}.
   1322    */
   1323   abstract class ExternalFailureVisitor extends FailingElementVisitor {}
   1324 
   1325   @Retention(RUNTIME)
   1326   @Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
   1327   @BindingAnnotation
   1328   public @interface SampleAnnotation { }
   1329 
   1330   public enum CoinSide { HEADS, TAILS }
   1331 
   1332   static class A<T> {
   1333     @Inject Stage stage;
   1334   }
   1335 
   1336   static class B<T> {
   1337     @Inject Stage stage;
   1338     B(T t) {}
   1339   }
   1340 
   1341   static class C {
   1342     @Inject @Named("foo") @SampleAnnotation String a;
   1343     C(@Named("bar") @SampleAnnotation Integer b) {}
   1344   }
   1345 }
   1346