diff options
Diffstat (limited to 'container-di/src/test/java/demo')
6 files changed, 369 insertions, 0 deletions
diff --git a/container-di/src/test/java/demo/Base.java b/container-di/src/test/java/demo/Base.java new file mode 100644 index 00000000000..7bc2c7b4404 --- /dev/null +++ b/container-di/src/test/java/demo/Base.java @@ -0,0 +1,66 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package demo; + +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.yahoo.component.ComponentId; +import com.yahoo.config.ConfigInstance; +import com.yahoo.container.di.ContainerTest; +import com.yahoo.container.di.componentgraph.core.ComponentGraph; +import com.yahoo.container.di.componentgraph.core.ComponentNode; +import com.yahoo.container.di.componentgraph.core.Node; +import com.yahoo.vespa.config.ConfigKey; +import org.junit.Before; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author tonytv + * @author gjoranv + */ +public class Base { + private ComponentGraph componentGraph; + private Injector injector; + private Map<ConfigKey<? extends ConfigInstance>, ConfigInstance> configs = + new HashMap<>(); + + @Before + public void createGraph() { + injector = Guice.createInjector(); + componentGraph = new ComponentGraph(0); + } + + public void register(Class<?> componentClass) { + componentGraph.add(mockComponentNode(componentClass)); + } + + public ComponentId toId(Class<?> componentClass) { + return ComponentId.fromString(componentClass.getName()); + } + + @SuppressWarnings("unchecked") + private Node mockComponentNode(Class<?> componentClass) { + return new ComponentNode(toId(componentClass), toId(componentClass).toString(), (Class<Object>)componentClass, null); + } + + public <T> T getInstance(Class<T> componentClass) { + return componentGraph.getInstance(componentClass); + } + + @SuppressWarnings("unchecked") + public void complete() { + componentGraph.complete(injector); + componentGraph.setAvailableConfigs(ContainerTest.convertMap(configs)); + } + + public void setInjector(Injector injector) { + this.injector = injector; + } + + @SuppressWarnings("unchecked") + public void addConfig(ConfigInstance configInstance, ComponentId id) { + configs.put(new ConfigKey<>((Class<ConfigInstance>)configInstance.getClass(), id.toString()), + configInstance); + } +} diff --git a/container-di/src/test/java/demo/ComponentConfigTest.java b/container-di/src/test/java/demo/ComponentConfigTest.java new file mode 100644 index 00000000000..5b3be2f549a --- /dev/null +++ b/container-di/src/test/java/demo/ComponentConfigTest.java @@ -0,0 +1,48 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package demo; + +import com.yahoo.config.test.ThreadPoolConfig; +import com.yahoo.container.di.componentgraph.Provider; +import org.junit.Test; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static org.junit.Assert.assertNotNull; + + +/** + * @author tonytv + * @author gjoranv + */ +public class ComponentConfigTest extends Base { + public static class ThreadPoolExecutorProvider implements Provider<Executor> { + private ExecutorService executor; + + public ThreadPoolExecutorProvider(ThreadPoolConfig config) { + executor = Executors.newFixedThreadPool(config.numThreads()); + } + + @Override + public Executor get() { + return executor; + } + + @Override + public void deconstruct() { + executor.shutdown(); + } + } + + @Test + public void require_that_non_components_can_be_configured() { + register(ThreadPoolExecutorProvider.class); + addConfig(new ThreadPoolConfig(new ThreadPoolConfig.Builder().numThreads(4)), + toId(ThreadPoolExecutorProvider.class)); + complete(); + + Executor executor = getInstance(Executor.class); + assertNotNull(executor); + } +} diff --git a/container-di/src/test/java/demo/ComponentRegistryTest.java b/container-di/src/test/java/demo/ComponentRegistryTest.java new file mode 100644 index 00000000000..193f65048ab --- /dev/null +++ b/container-di/src/test/java/demo/ComponentRegistryTest.java @@ -0,0 +1,42 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package demo; + +import com.yahoo.component.AbstractComponent; +import com.yahoo.component.provider.ComponentRegistry; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; + + +/** + * @author tonytv + * @author gjoranv + */ +public class ComponentRegistryTest extends Base { + public static class SearchHandler extends AbstractComponent { + private final ComponentRegistry<Searcher> searchers; + + public SearchHandler(ComponentRegistry<Searcher> searchers) { + this.searchers = searchers; + } + } + + public static class Searcher extends AbstractComponent {} + + public static class FooSearcher extends Searcher {} + public static class BarSearcher extends Searcher {} + + @Test + public void require_that_component_registry_can_be_injected() { + register(SearchHandler.class); + register(FooSearcher.class); + register(BarSearcher.class); + complete(); + + SearchHandler handler = getInstance(SearchHandler.class); + + ComponentRegistry<Searcher> searchers = handler.searchers; + assertNotNull(searchers.getComponent(toId(FooSearcher.class))); + assertNotNull(searchers.getComponent(toId(BarSearcher.class))); + } +} diff --git a/container-di/src/test/java/demo/ContainerTestBase.java b/container-di/src/test/java/demo/ContainerTestBase.java new file mode 100644 index 00000000000..fdc73913052 --- /dev/null +++ b/container-di/src/test/java/demo/ContainerTestBase.java @@ -0,0 +1,74 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package demo; + +import com.google.inject.Guice; +import com.yahoo.component.ComponentSpecification; +import com.yahoo.container.bundle.BundleInstantiationSpecification; +import com.yahoo.config.FileReference; +import com.yahoo.container.di.CloudSubscriberFactory; +import com.yahoo.container.di.Container; +import com.yahoo.container.di.ContainerTest; +import com.yahoo.container.di.Osgi; +import com.yahoo.container.di.componentgraph.core.ComponentGraph; +import org.junit.Before; +import org.osgi.framework.Bundle; +import scala.collection.*; +import scala.collection.immutable.*; +import scala.collection.immutable.Set; + +import java.util.Collection; +import java.util.List; + +/** + * @author tonytv + * @author gjoranv + */ +public class ContainerTestBase extends ContainerTest { + private ComponentGraph componentGraph; + + @Before + public void createGraph() { + componentGraph = new ComponentGraph(0); + } + + public void complete() { + try { + Container container = new Container( + new CloudSubscriberFactory(dirConfigSource().configSource()), + dirConfigSource().configId(), + new ContainerTest.TestDeconstructor(), + new Osgi() { + @SuppressWarnings("unchecked") + @Override + public Class<Object> resolveClass(BundleInstantiationSpecification spec) { + try { + return (Class<Object>) Class.forName(spec.classId.getName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Override + public BundleClasses getBundleClasses(ComponentSpecification bundle, + Set<String> packagesToScan) { + throw new UnsupportedOperationException("getBundleClasses not supported"); + } + + @Override + public void useBundles(Collection<FileReference> bundles) {} + + @Override + public Bundle getBundle(ComponentSpecification spec) { + throw new UnsupportedOperationException("getBundle not supported."); + } + }); + componentGraph = container.runOnce(componentGraph, Guice.createInjector()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public <T> T getInstance(Class<T> componentClass) { + return componentGraph.getInstance(componentClass); + } +} diff --git a/container-di/src/test/java/demo/DeconstructTest.java b/container-di/src/test/java/demo/DeconstructTest.java new file mode 100644 index 00000000000..10e9844f6de --- /dev/null +++ b/container-di/src/test/java/demo/DeconstructTest.java @@ -0,0 +1,35 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package demo; + +import com.yahoo.container.di.ContainerTest; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +/** + * @author tonytv + * @author gjoranv + */ +public class DeconstructTest extends ContainerTestBase { + public static class DeconstructableComponent extends ContainerTest.DestructableComponent { + private boolean isDeconstructed = false; + + @Override + public void deconstruct() { + isDeconstructed = true; + } + } + + @Test + public void require_that_unused_components_are_deconstructed() { + writeBootstrapConfigs("d1", DeconstructableComponent.class); + complete(); + + DeconstructableComponent d1 = getInstance(DeconstructableComponent.class); + + writeBootstrapConfigs("d2", DeconstructableComponent.class); + complete(); + + assertTrue(d1.isDeconstructed); + } +} diff --git a/container-di/src/test/java/demo/FallbackToGuiceInjectorTest.java b/container-di/src/test/java/demo/FallbackToGuiceInjectorTest.java new file mode 100644 index 00000000000..3d5550ab0a3 --- /dev/null +++ b/container-di/src/test/java/demo/FallbackToGuiceInjectorTest.java @@ -0,0 +1,104 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package demo; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.google.inject.name.Named; +import com.google.inject.name.Names; +import com.yahoo.component.AbstractComponent; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertNotNull; + +/** + * @author tonytv + * @author gjoranv + */ +@SuppressWarnings("unused") +public class FallbackToGuiceInjectorTest extends Base { + public static class MyComponent extends AbstractComponent { + private final String url; + private final Executor executor; + + @Inject + public MyComponent(@Named("url") String url, Executor executor) { + this.url = url; + this.executor = executor; + } + + public MyComponent() { + throw new RuntimeException("Constructor annotated with @Inject is preferred."); + } + } + + public static class ComponentTakingDefaultString{ + private final String injectedString; + + public ComponentTakingDefaultString(String empty_string_created_by_guice) { + this.injectedString = empty_string_created_by_guice; + } + } + + public static class ComponentThatCannotBeConstructed { + public ComponentThatCannotBeConstructed(Integer cannot_be_injected_because_Integer_has_no_default_ctor) { } + } + + @Rule + public final ExpectedException exception = ExpectedException.none(); + + @Test + public void guice_injector_is_used_when_no_global_component_exists() { + setInjector( + Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + bind(Executor.class).toInstance(Executors.newSingleThreadExecutor()); + bind(String.class).annotatedWith(Names.named("url")).toInstance("http://yahoo.com"); + } + })); + + register(MyComponent.class); + complete(); + + MyComponent component = getInstance(MyComponent.class); + assertThat(component.url, is("http://yahoo.com")); + assertNotNull(component.executor); + } + + @Test + public void guice_injector_creates_a_new_instance_with_default_ctor_when_no_explicit_binding_exists() { + setInjector(emptyGuiceInjector()); + register(ComponentTakingDefaultString.class); + complete(); + + ComponentTakingDefaultString component = getInstance(ComponentTakingDefaultString.class); + assertThat(component.injectedString, is("")); + } + + @Test + public void guice_injector_fails_when_no_explicit_binding_exists_and_class_has_no_default_ctor() { + setInjector(emptyGuiceInjector()); + register(ComponentThatCannotBeConstructed.class); + + exception.expect(RuntimeException.class); + exception.expectMessage("When resolving dependencies of 'demo.FallbackToGuiceInjectorTest$ComponentThatCannotBeConstructed'"); + complete(); + } + + private Injector emptyGuiceInjector() { + return Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + } + }); + } +} |