/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.service.servicemanager;
import org.junit.Test;
import com.foundationdb.server.error.CircularDependencyException;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static org.junit.Assert.fail;
/**
* <p>Tests the startup of various services. Here's the dependency graph:
* <pre>
* A <---+
* ↙ ↘ ¦
* B C ¦
* ↙ ↓ ↓ ¦
* E D ↓ ¦
* ↖ ↘ ↙ ¦
* ← ← F - - - +
* </pre>
* </p>
* <p>The dotted line from F to A represents a circular dependency. We don't allow this, so most tests use an
* implementation of F that does <em>not</em> depend on A. The one that does, we're looking for an exception.</p>
*/
public final class GuicerDITest {
@Test
public void nonCircularFromA() {
standardTester()
.startAndStop(A.class)
.check(AImpl.class, BImpl.class, CImpl.class, EImpl.class, DImpl.class, FImpl.class)
.checkDependencies(AImpl.class, BImpl.class, DImpl.class, CImpl.class, FImpl.class, EImpl.class)
.checkDependencies(BImpl.class, DImpl.class, FImpl.class, EImpl.class)
.checkDependencies(CImpl.class, FImpl.class, EImpl.class)
.checkDependencies(DImpl.class, FImpl.class, EImpl.class)
.checkDependencies(EImpl.class)
.checkDependencies(FImpl.class, EImpl.class)
;
}
@Test
public void nonCircularFromF() {
standardTester()
.startAndStop(F.class)
.check(EImpl.class, FImpl.class)
.checkDependencies(EImpl.class)
.checkDependencies(FImpl.class, EImpl.class)
;
}
@Test
public void nonCircularFromE() {
standardTester()
.startAndStop(E.class)
.check(EImpl.class)
;
}
@Test(expected = CircularDependencyException.class)
public void circular() {
GuiceInjectionTester tester = standardTester().bind(F.class, CircularF.class).check();
try {
tester.startAndStop(F.class);
fail("expected a CircularDependencyException");
} catch (CircularDependencyException e) {
tester.check();
throw e;
}
}
@Test
public void fieldInjection() {
new GuiceInjectionTester()
.bind(FieldInjectionA.class, FieldInjectionAImpl.class)
.bind(FieldInjectionB.class, FieldInjectionBImpl.class)
.startAndStop(FieldInjectionA.class)
.check(FieldInjectionAImpl.class, FieldInjectionBImpl.class)
.checkDependencies(FieldInjectionAImpl.class, FieldInjectionBImpl.class);
}
@Test
public void methodInjection() {
new GuiceInjectionTester()
.bind(MethodInjectionA.class, MethodInjectionAImpl.class)
.bind(MethodInjectionB.class, MethodInjectionBImpl.class)
.startAndStop(MethodInjectionA.class)
.check(MethodInjectionAImpl.class, MethodInjectionBImpl.class)
.checkDependencies(MethodInjectionAImpl.class, MethodInjectionBImpl.class);
}
private static GuiceInjectionTester standardTester() {
return new GuiceInjectionTester()
.bind(A.class, AImpl.class)
.bind(B.class, BImpl.class)
.bind(C.class, CImpl.class)
.bind(D.class, DImpl.class)
.bind(E.class, EImpl.class)
.bind(F.class, FImpl.class);
}
public static abstract class Interesting {
protected Interesting(Object... dependencies) {
this.dependencies = Arrays.asList(dependencies);
}
@Override
public String toString() {
List<String> dependencyClasses = new ArrayList<>();
for (Object dependency : dependencies) {
dependencyClasses.add(dependency.getClass().getSimpleName());
}
return getClass().getSimpleName() + " directly depends on " + dependencyClasses;
}
private List<?> dependencies;
}
public interface A {}
public interface B {}
public interface C {}
public interface D {}
public interface E {}
public interface F {}
public static class AImpl extends Interesting implements A {
@Inject
public AImpl(B b, C c) {
super(b, c);
}
}
public static class BImpl extends Interesting implements B {
@Inject
public BImpl(D d, E e) {
super(d, e);
}
}
public static class CImpl extends Interesting implements C {
@Inject
public CImpl(F f) {
super(f);
}
}
public static class DImpl extends Interesting implements D {
@Inject
public DImpl(F f) {
super(f);
}
}
public static class EImpl extends Interesting implements E {
}
public static class FImpl extends Interesting implements F {
@Inject
public FImpl(E e) {
super(e);
}
}
public static class CircularF extends Interesting implements F {
@Inject
public CircularF(A a) {
super(a);
}
}
public interface FieldInjectionA {}
public interface FieldInjectionB {}
public static class FieldInjectionAImpl implements FieldInjectionA {
@SuppressWarnings("unused") @Inject private FieldInjectionB b;
}
public static class FieldInjectionBImpl implements FieldInjectionB {}
public interface MethodInjectionA {}
public interface MethodInjectionB {}
public static class MethodInjectionAImpl implements MethodInjectionA {
@SuppressWarnings("unused") @Inject
private void setDependency(MethodInjectionB b) {
assert b != null;
}
}
public static class MethodInjectionBImpl implements MethodInjectionB {}
@Test
public void managerInjection() {
M m = new MImpl(1.0);
new GuiceInjectionTester()
.manager(M.class, m)
.bind(N.class, NImpl.class)
.startAndStop(N.class);
assert (m.wasSeen());
}
public interface M extends ServiceManagerBase {
boolean wasSeen();
void seen();
}
public interface N {
}
public static class MImpl implements M {
// No no-arg ctor.
public MImpl(double d) {
}
private boolean seen = false;
@Override
public boolean wasSeen() {
return seen;
}
@Override
public void seen() {
seen = true;
}
}
public static class NImpl implements N {
@Inject
public NImpl(M mgr) {
mgr.seen();
}
}
}