/** * This test is in an 'external' module to bypass the restriction that governator * internal modules cannot be picked up via module depdency */ package com.netflix.external.governator.guice.modules; import com.google.common.collect.Lists; import com.google.inject.AbstractModule; import com.google.inject.Inject; import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.Singleton; import com.google.inject.Stage; import com.google.inject.TypeLiteral; import com.netflix.governator.LifecycleInjectorBuilderProvider; import com.netflix.governator.guice.BootstrapBinder; import com.netflix.governator.guice.BootstrapModule; import com.netflix.governator.guice.LifecycleInjectorBuilder; import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.concurrent.atomic.AtomicLong; @RunWith(DataProviderRunner.class) public class ModuleDepdenciesTest extends LifecycleInjectorBuilderProvider { private static final Logger LOG = LoggerFactory.getLogger(ModuleDepdenciesTest.class); private static final TypeLiteral<List<Integer>> LIST_TYPE_LITERAL = new TypeLiteral<List<Integer>>() { }; private static AtomicLong counter = new AtomicLong(0); @Singleton public static class ModuleA extends AbstractModule { public ModuleA() { LOG.info("ModuleA created"); } @Override protected void configure() { LOG.info("ConfigureA"); counter.incrementAndGet(); } } @After public void afterEachTest() { counter.set(0); } @Singleton public static class ModuleB extends AbstractModule { @Inject public ModuleB(ModuleA a) { LOG.info("ModuleB created"); } @Override protected void configure() { LOG.info("ConfigureB"); counter.incrementAndGet(); } } @Test @UseDataProvider("builders") public void testModuleDependency(LifecycleInjectorBuilder lifecycleInjectorBuilder) throws Exception { lifecycleInjectorBuilder.withAdditionalModuleClasses(ModuleB.class).build().createInjector(); } @Singleton public static class A1 { @Inject public A1(List<Integer> list) { list.add(1); } } @Singleton public static class A2 { @Inject public A2(List<Integer> list) { list.add(2); } } @Singleton public static class A3 { @Inject public A3(List<Integer> list) { list.add(3); } } @Singleton public static class A4 { @Inject public A4(List<Integer> list) { list.add(4); } } @Singleton public static class ModuleA1 extends AbstractModule { protected void configure() { bind(A1.class); } } @Singleton public static class ModuleA2 extends AbstractModule { @Inject public ModuleA2(ModuleA1 moduleA1) { } public ModuleA2() { } protected void configure() { bind(A2.class); } } @Singleton public static class ModuleA3 extends AbstractModule { public ModuleA3() { } @Inject public ModuleA3(ModuleA2 moduleA3) { } protected void configure() { bind(A3.class); } } @Singleton public static class ModuleA4 extends AbstractModule { public ModuleA4() { } @Inject public ModuleA4(ModuleA3 moduleA3) { } protected void configure() { bind(A4.class); } } @Test @UseDataProvider("builders") public void confirmBindingSingletonOrder(LifecycleInjectorBuilder lifecycleInjectorBuilder) throws Exception { final List<Integer> actual = Lists.newArrayList(); final List<Module> modules = Lists.<Module>newArrayList(new ModuleA1(), new ModuleA2(), new ModuleA3(), new ModuleA4()); BootstrapModule bootstrap = new BootstrapModule() { @Override public void configure(BootstrapBinder binder) { binder.bind(LIST_TYPE_LITERAL).toInstance(actual); } }; // Confirm that singletons are created in binding order final List<Integer> expected = Lists.newArrayList(1, 2, 3, 4); Injector injector = lifecycleInjectorBuilder.inStage(Stage.PRODUCTION).withModules(modules).withBootstrapModule(bootstrap).build().createInjector(); Assert.assertEquals(actual, expected); } @Test @UseDataProvider("builders") public void confirmBindingReverseSingletonOrder(LifecycleInjectorBuilder lifecycleInjectorBuilder) throws Exception { final List<Integer> actual = Lists.newArrayList(); final List<Module> modules = Lists.<Module>newArrayList(new ModuleA1(), new ModuleA2(), new ModuleA3(), new ModuleA4()); BootstrapModule bootstrap = new BootstrapModule() { @Override public void configure(BootstrapBinder binder) { binder.bind(LIST_TYPE_LITERAL).toInstance(actual); } }; // Reverse the order of modules in the list to confirm that singletons // are now created in reverse order final List<Integer> expected = Lists.newArrayList(1, 2, 3, 4); Injector injector = lifecycleInjectorBuilder.inStage(Stage.PRODUCTION).withModules(Lists.reverse(modules)).withBootstrapModule(bootstrap).build().createInjector(); LOG.info(actual.toString()); Assert.assertEquals(actual, Lists.reverse(expected)); } @Test @UseDataProvider("builders") public void confirmMultiWithModuleClasses(LifecycleInjectorBuilder lifecycleInjectorBuilder) { final List<Integer> actual = Lists.newArrayList(); BootstrapModule bootstrap = new BootstrapModule() { @Override public void configure(BootstrapBinder binder) { binder.bind(LIST_TYPE_LITERAL).toInstance(actual); } }; Injector injector = lifecycleInjectorBuilder .inStage(Stage.PRODUCTION) .withModuleClasses(ModuleA2.class, ModuleA3.class) .withBootstrapModule(bootstrap).build().createInjector(); final List<Integer> expected = Lists.newArrayList(1, 2, 3); LOG.info(actual.toString()); Assert.assertEquals(actual, expected); } }