/**
*
* Copyright (c) 2006-2017, Speedment, Inc. All Rights Reserved.
*
* 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.speedment.common.injector;
import static com.speedment.common.injector.State.INITIALIZED;
import static com.speedment.common.injector.State.RESOLVED;
import static com.speedment.common.injector.State.STARTED;
import static com.speedment.common.injector.State.STOPPED;
import com.speedment.common.injector.annotation.Config;
import com.speedment.common.injector.annotation.ExecuteBefore;
import com.speedment.common.injector.annotation.InjectKey;
import com.speedment.common.injector.exception.NoDefaultConstructorException;
import com.speedment.common.injector.test_a.StringIdentityMapper;
import com.speedment.common.injector.test_a.TypeMapperComponent;
import com.speedment.common.injector.test_b.A;
import com.speedment.common.injector.test_b.B;
import com.speedment.common.injector.test_b.C;
import com.speedment.common.injector.test_c.ChildType;
import com.speedment.common.injector.test_c.ParentType;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.Test;
/**
*
* @author Emil Forslund
* @since 1.0.0
*/
public class InjectorTest {
@Test
public void testSimpleInjector() {
final Injector injector;
try {
injector = Injector.builder()
.withComponent(StringIdentityMapper.class)
.withComponent(TypeMapperComponent.class)
.build();
} catch (final InstantiationException ex) {
throw new RuntimeException(
"Failed to instantiate class.", ex
);
}
final StringIdentityMapper mapper = injector.getOrThrow(StringIdentityMapper.class);
final TypeMapperComponent mappers = injector.getOrThrow(TypeMapperComponent.class);
assertNotNull(mapper);
assertNotNull(mappers);
assertEquals(mapper, mappers.toDatabaseTypeMappers().get(String.class));
assertEquals(mapper, mappers.toJavaTypeMappers().get(String.class));
}
@Test
public void testPotentialCyclicDependency() {
final Injector injector;
try {
injector = Injector.builder()
.withComponent(A.class)
.withComponent(B.class)
.withComponent(C.class)
.build();
} catch (final InstantiationException ex) {
throw new RuntimeException(
"Failed to instantiate class.", ex
);
}
assertNotNull(injector.getOrThrow(A.class).b);
assertNotNull(injector.getOrThrow(A.class).c);
assertNotNull(injector.getOrThrow(B.class).a);
assertNotNull(injector.getOrThrow(B.class).c);
assertNotNull(injector.getOrThrow(C.class).a);
assertNotNull(injector.getOrThrow(C.class).b);
}
@Test
public void testInheritance() {
final Injector injector;
try {
injector = Injector.builder()
.withComponent(A.class)
.withComponent(B.class)
.withComponent(C.class)
.withComponent(ChildType.class)
.build();
} catch (final NoDefaultConstructorException
| InstantiationException ex) {
throw new RuntimeException(
"Failed to instantiate class.", ex
);
}
assertNotNull(injector.getOrThrow(ParentType.class).a);
assertNotNull(injector.getOrThrow(ChildType.class).b);
}
@Test
public void testKeyMultiples() {
final Injector injector;
try {
injector = Injector.builder()
.withComponent(Bar.class)
.withComponent(Baz.class)
.build();
} catch (final NoDefaultConstructorException
| InstantiationException ex) {
throw new RuntimeException(
"Failed to instantiate class.", ex
);
}
assertNotNull("Make sure Foo has an implementation", injector.get(Foo.class).orElse(null));
assertNotNull("Make sure Bar had an implementation", injector.get(Bar.class).orElse(null));
assertNotNull("Make sure Baz had an implementation", injector.get(Baz.class).orElse(null));
assertEquals("Make sure the default implementation is Baz.", Baz.class, injector.get(Foo.class).get().getClass());
assertEquals("Make sure the Bar can still be accessed", Bar.class, injector.get(Bar.class).get().getClass());
}
@InjectKey(Foo.class)
private interface Foo {}
private final static class Bar implements Foo {}
private final static class Baz implements Foo {}
@Test
public void testKeyWithoutOverwrite() {
final Injector injector;
try {
injector = Injector.builder()
.withComponent(Bar.class)
.withComponent(Baz.class)
.withComponent(FooNoOverwrite.class)
.build();
} catch (final NoDefaultConstructorException
| InstantiationException ex) {
throw new RuntimeException(
"Failed to instantiate class.", ex
);
}
assertNotNull("Make sure Foo has an implementation", injector.get(Foo.class).orElse(null));
assertNotNull("Make sure Bar had an implementation", injector.get(Bar.class).orElse(null));
assertNotNull("Make sure Baz had an implementation", injector.get(Baz.class).orElse(null));
assertNotNull("Make sure Baz had an implementation", injector.get(FooNoOverwrite.class).orElse(null));
assertEquals("Make sure the default implementation is FooNoOverwrite.", FooNoOverwrite.class, injector.get(Foo.class).get().getClass());
assertEquals("Make sure FooNoOverwrite can be accessed directly.", FooNoOverwrite.class, injector.get(FooNoOverwrite.class).get().getClass());
assertEquals("Make sure the Bar can still be accessed", Bar.class, injector.get(Bar.class).get().getClass());
assertEquals("Make sure the Baz can still be accessed", Baz.class, injector.get(Baz.class).get().getClass());
}
@InjectKey(value=Foo.class, overwrite=false)
private final static class FooNoOverwrite implements Foo {}
private final static class ClassWithConfig {
private @Config(name="a", value="example") String defaultString;
private @Config(name="b", value="-104726") int defaultInt;
private @Config(name="c", value="0.43472") float defaultFloat;
private @Config(name="d", value="false") boolean defaultBoolean;
private @Config(name="e", value="example") String overridenString;
private @Config(name="f", value="-104726") int overridenInt;
private @Config(name="g", value="0.43472") float overridenFloat;
private @Config(name="h", value="false") boolean overridenBoolean;
}
private ClassWithConfig configTest;
@Before
public void setupConfigTest() throws InstantiationException {
configTest = new ClassWithConfig();
final Injector injector = Injector.builder()
.withParam("e", "anotherExample")
.withParam("f", "56629")
.withParam("g", "-476.443")
.withParam("h", "true")
.build();
injector.inject(configTest);
}
@Test
public void testDefaultConfig() {
assertEquals("Test default string param: ", "example", configTest.defaultString);
assertEquals("Test default int param: ", -104726, configTest.defaultInt);
assertEquals("Test default float param: ", 0.43472f, configTest.defaultFloat, 0.00000001f);
assertEquals("Test default boolean param: ", false, configTest.defaultBoolean);
}
@Test
public void testOverridenConfig() {
assertEquals("Test overriden string param: ", "anotherExample", configTest.overridenString);
assertEquals("Test overriden int param: ", 56629, configTest.overridenInt);
assertEquals("Test overriden float param: ", -476.443f, configTest.overridenFloat, 0.00000001f);
assertEquals("Test overriden boolean param: ", true, configTest.overridenBoolean);
}
private static abstract class AbstractComponent {
protected final AtomicInteger counter;
AbstractComponent() {
counter = new AtomicInteger();
}
public int getCount() {
return counter.get();
}
@ExecuteBefore(INITIALIZED)
void parentInitialized() {
counter.incrementAndGet();
}
@ExecuteBefore(RESOLVED)
void parentResolved() {
counter.incrementAndGet();
}
@ExecuteBefore(STARTED)
void parentStarted() {
counter.incrementAndGet();
}
}
private static final class ImplementingComponent extends AbstractComponent {
@ExecuteBefore(INITIALIZED)
void childInitialized() {
counter.incrementAndGet();
}
@ExecuteBefore(RESOLVED)
void childResolved() {
counter.incrementAndGet();
}
@ExecuteBefore(STARTED)
void childStarted() {
counter.incrementAndGet();
}
}
@Test
public void testParentChildExecutors() {
try {
final Injector injector = Injector.builder()
.withComponent(ImplementingComponent.class)
.build();
final ImplementingComponent component =
injector.getOrThrow(ImplementingComponent.class);
assertEquals("Make sure all executors was executed: ", 6, component.getCount());
} catch (final InstantiationException ex) {
fail("InstantiationException!");
}
}
private static final class ExecutableComponent {
private final Set<String> actionsInvoked = new HashSet<>();
private int count;
public void set(String action) {
actionsInvoked.add(action);
count++;
}
@ExecuteBefore(INITIALIZED)
public void onInit() {
actionsInvoked.add("q");
count++;
}
@ExecuteBefore(STOPPED)
public void onStop() {
actionsInvoked.add("z");
count++;
}
public Set<String> getActionsInvoked() {
return actionsInvoked;
}
public int getCount() {
return count;
}
}
@Test
public void testInvokeExecutors() {
try {
final Injector injector = Injector.builder()
.withComponent(ExecutableComponent.class)
.beforeInitialized(ExecutableComponent.class, ec -> ec.set("a"))
.beforeInitialized(ExecutableComponent.class, ec -> ec.set("b"))
.beforeResolved(ExecutableComponent.class, ec -> ec.set("c"))
.beforeStopped(ExecutableComponent.class, ec -> ec.set("d"))
.beforeStarted(ExecutableComponent.class, ec -> ec.set("e"))
.beforeStarted(ExecutableComponent.class, ec -> ec.set("f"))
.beforeStopped(ExecutableComponent.class, ec -> ec.set("g"))
.beforeInitialized(ExecutableComponent.class, ec -> ec.set("h"))
.build();
final ExecutableComponent c =
injector.getOrThrow(ExecutableComponent.class);
assertTrue("Make sure 'a' was added before stop: ", c.getActionsInvoked().contains("a"));
assertTrue("Make sure 'b' was added before stop: ", c.getActionsInvoked().contains("b"));
assertTrue("Make sure 'c' was added before stop: ", c.getActionsInvoked().contains("c"));
assertFalse("Make sure 'd' was NOT added before stop: ", c.getActionsInvoked().contains("d"));
assertTrue("Make sure 'e' was added before stop: ", c.getActionsInvoked().contains("e"));
assertTrue("Make sure 'f' was added before stop: ", c.getActionsInvoked().contains("f"));
assertFalse("Make sure 'g' was NOT added before stop: ", c.getActionsInvoked().contains("g"));
assertTrue("Make sure 'h' was added before stop: ", c.getActionsInvoked().contains("h"));
assertTrue("Make sure 'q' was added before stop: ", c.getActionsInvoked().contains("q"));
assertFalse("Make sure 'z' was NOT added before stop: ", c.getActionsInvoked().contains("z"));
assertEquals("Make sure the total count is right before stop: ", 7, c.getCount());
injector.stop();
assertTrue("Make sure 'a' was added after stop: ", c.getActionsInvoked().contains("a"));
assertTrue("Make sure 'b' was added after stop: ", c.getActionsInvoked().contains("b"));
assertTrue("Make sure 'c' was added after stop: ", c.getActionsInvoked().contains("c"));
assertTrue("Make sure 'd' was added after stop: ", c.getActionsInvoked().contains("d"));
assertTrue("Make sure 'e' was added after stop: ", c.getActionsInvoked().contains("e"));
assertTrue("Make sure 'f' was added after stop: ", c.getActionsInvoked().contains("f"));
assertTrue("Make sure 'g' was added after stop: ", c.getActionsInvoked().contains("g"));
assertTrue("Make sure 'h' was added after stop: ", c.getActionsInvoked().contains("h"));
assertTrue("Make sure 'q' was added after stop: ", c.getActionsInvoked().contains("q"));
assertTrue("Make sure 'z' was added after stop: ", c.getActionsInvoked().contains("z"));
assertEquals("Make sure the total count is right after stop: ", 10, c.getCount());
} catch (final InstantiationException ex) {
fail("InstantiationException!");
}
}
}