/* * Copyright (C) 2006 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.inject; import static com.google.inject.Asserts.assertContains; import static com.google.inject.Asserts.assertNotSerializable; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.io.IOException; import java.lang.annotation.Retention; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicReference; import junit.framework.TestCase; /** @author crazybob@google.com (Bob Lee) */ public class InjectorTest extends TestCase { @Retention(RUNTIME) @BindingAnnotation @interface Other {} @Retention(RUNTIME) @BindingAnnotation @interface S {} @Retention(RUNTIME) @BindingAnnotation @interface I {} public void testToStringDoesNotInfinitelyRecurse() { Injector injector = Guice.createInjector(Stage.TOOL); injector.toString(); injector.getBinding(Injector.class).toString(); } public void testProviderMethods() throws CreationException { final SampleSingleton singleton = new SampleSingleton(); final SampleSingleton other = new SampleSingleton(); Injector injector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { bind(SampleSingleton.class).toInstance(singleton); bind(SampleSingleton.class).annotatedWith(Other.class).toInstance(other); } }); assertSame(singleton, injector.getInstance(Key.get(SampleSingleton.class))); assertSame(singleton, injector.getInstance(SampleSingleton.class)); assertSame(other, injector.getInstance(Key.get(SampleSingleton.class, Other.class))); } static class SampleSingleton {} public void testInjection() throws CreationException { Injector injector = createFooInjector(); Foo foo = injector.getInstance(Foo.class); assertEquals("test", foo.s); assertEquals("test", foo.bar.getTee().getS()); assertSame(foo.bar, foo.copy); assertEquals(5, foo.i); assertEquals(5, foo.bar.getI()); // Test circular dependency. assertSame(foo.bar, foo.bar.getTee().getBar()); } private Injector createFooInjector() throws CreationException { return Guice.createInjector( new AbstractModule() { @Override protected void configure() { bind(Bar.class).to(BarImpl.class); bind(Tee.class).to(TeeImpl.class); bindConstant().annotatedWith(S.class).to("test"); bindConstant().annotatedWith(I.class).to(5); } }); } public void testGetInstance() throws CreationException { Injector injector = createFooInjector(); Bar bar = injector.getInstance(Key.get(Bar.class)); assertEquals("test", bar.getTee().getS()); assertEquals(5, bar.getI()); } public void testIntAndIntegerAreInterchangeable() throws CreationException { Injector injector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { bindConstant().annotatedWith(I.class).to(5); } }); IntegerWrapper iw = injector.getInstance(IntegerWrapper.class); assertEquals(5, (int) iw.i); } public void testInjectorApiIsNotSerializable() throws IOException { Injector injector = Guice.createInjector(); assertNotSerializable(injector); assertNotSerializable(injector.getProvider(String.class)); assertNotSerializable(injector.getBinding(String.class)); for (Binding<?> binding : injector.getBindings().values()) { assertNotSerializable(binding); } } static class IntegerWrapper { @Inject @I Integer i; } static class Foo { @Inject Bar bar; @Inject Bar copy; @Inject @S String s; int i; @Inject void setI(@I int i) { this.i = i; } } interface Bar { Tee getTee(); int getI(); } @Singleton static class BarImpl implements Bar { @Inject @I int i; Tee tee; @Inject void initialize(Tee tee) { this.tee = tee; } @Override public Tee getTee() { return tee; } @Override public int getI() { return i; } } interface Tee { String getS(); Bar getBar(); } static class TeeImpl implements Tee { final String s; @Inject Bar bar; @Inject TeeImpl(@S String s) { this.s = s; } @Override public String getS() { return s; } @Override public Bar getBar() { return bar; } } public void testInjectStatics() throws CreationException { Guice.createInjector( new AbstractModule() { @Override protected void configure() { bindConstant().annotatedWith(S.class).to("test"); bindConstant().annotatedWith(I.class).to(5); requestStaticInjection(Static.class); } }); assertEquals("test", Static.s); assertEquals(5, Static.i); } public void testInjectStaticInterface() { try { Guice.createInjector( new AbstractModule() { @Override protected void configure() { requestStaticInjection(Interface.class); } }); fail(); } catch (CreationException ce) { assertEquals(1, ce.getErrorMessages().size()); Asserts.assertContains( ce.getMessage(), "1) " + Interface.class.getName() + " is an interface, but interfaces have no static injection points.", "at " + InjectorTest.class.getName(), "configure"); } } private static interface Interface {} static class Static { @Inject @I static int i; static String s; @Inject static void setS(@S String s) { Static.s = s; } } public void testPrivateInjection() throws CreationException { Injector injector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { bind(String.class).toInstance("foo"); bind(int.class).toInstance(5); } }); Private p = injector.getInstance(Private.class); assertEquals("foo", p.fromConstructor); assertEquals(5, p.fromMethod); } static class Private { String fromConstructor; int fromMethod; @Inject private Private(String fromConstructor) { this.fromConstructor = fromConstructor; } @Inject private void setInt(int i) { this.fromMethod = i; } } public void testProtectedInjection() throws CreationException { Injector injector = Guice.createInjector( new AbstractModule() { @Override protected void configure() { bind(String.class).toInstance("foo"); bind(int.class).toInstance(5); } }); Protected p = injector.getInstance(Protected.class); assertEquals("foo", p.fromConstructor); assertEquals(5, p.fromMethod); } static class Protected { String fromConstructor; int fromMethod; @Inject protected Protected(String fromConstructor) { this.fromConstructor = fromConstructor; } @Inject protected void setInt(int i) { this.fromMethod = i; } } public void testInstanceInjectionHappensAfterFactoriesAreSetUp() { Guice.createInjector( new AbstractModule() { @Override protected void configure() { bind(Object.class) .toInstance( new Object() { @Inject Runnable r; }); bind(Runnable.class).to(MyRunnable.class); } }); } public void testSubtypeNotProvided() { try { Guice.createInjector().getInstance(Money.class); fail(); } catch (ProvisionException expected) { assertContains( expected.getMessage(), Tree.class.getName() + " doesn't provide instances of " + Money.class.getName(), "while locating ", Tree.class.getName(), "while locating ", Money.class.getName()); } } public void testNotASubtype() { try { Guice.createInjector().getInstance(PineTree.class); fail(); } catch (ConfigurationException expected) { assertContains( expected.getMessage(), Tree.class.getName() + " doesn't extend " + PineTree.class.getName(), "while locating ", PineTree.class.getName()); } } public void testRecursiveImplementationType() { try { Guice.createInjector().getInstance(SeaHorse.class); fail(); } catch (ConfigurationException expected) { assertContains( expected.getMessage(), "@ImplementedBy points to the same class it annotates.", "while locating ", SeaHorse.class.getName()); } } public void testRecursiveProviderType() { try { Guice.createInjector().getInstance(Chicken.class); fail(); } catch (ConfigurationException expected) { assertContains( expected.getMessage(), "@ProvidedBy points to the same class it annotates", "while locating ", Chicken.class.getName()); } } static class MyRunnable implements Runnable { @Override public void run() {} } @ProvidedBy(Tree.class) static class Money {} static class Tree implements Provider<Object> { @Override public Object get() { return "Money doesn't grow on trees"; } } @ImplementedBy(Tree.class) static class PineTree extends Tree {} @ImplementedBy(SeaHorse.class) static class SeaHorse {} @ProvidedBy(Chicken.class) static class Chicken implements Provider<Chicken> { @Override public Chicken get() { return this; } } public void testJitBindingFromAnotherThreadDuringInjection() { final ExecutorService executorService = Executors.newSingleThreadExecutor(); final AtomicReference<JustInTime> got = new AtomicReference<JustInTime>(); Guice.createInjector( new AbstractModule() { @Override protected void configure() { requestInjection( new Object() { @Inject void initialize(final Injector injector) throws ExecutionException, InterruptedException { Future<JustInTime> future = executorService.submit( new Callable<JustInTime>() { @Override public JustInTime call() throws Exception { return injector.getInstance(JustInTime.class); } }); got.set(future.get()); } }); } }); assertNotNull(got.get()); } static class JustInTime {} }