Package com.google.gwt.inject.rebind.resolution

Source Code of com.google.gwt.inject.rebind.resolution.BindingResolverTest$StandardTree

/*
* Copyright (C) 2011 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.gwt.inject.rebind.resolution;

import static com.google.gwt.inject.rebind.resolution.TestUtils.asyncProviderFoo;
import static com.google.gwt.inject.rebind.resolution.TestUtils.bar;
import static com.google.gwt.inject.rebind.resolution.TestUtils.baz;
import static com.google.gwt.inject.rebind.resolution.TestUtils.foo;
import static com.google.gwt.inject.rebind.resolution.TestUtils.fooImpl;
import static com.google.gwt.inject.rebind.resolution.TestUtils.providerBar;
import static com.google.gwt.inject.rebind.resolution.TestUtils.providerBaz;
import static com.google.gwt.inject.rebind.resolution.TestUtils.providerFoo;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.createControl;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.inject.rebind.ErrorManager;
import com.google.gwt.inject.rebind.GinjectorBindings;
import com.google.gwt.inject.rebind.binding.Binding;
import com.google.gwt.inject.rebind.binding.BindingFactory;
import com.google.gwt.inject.rebind.binding.Context;
import com.google.gwt.inject.rebind.binding.Dependency;
import com.google.gwt.inject.rebind.binding.ExposedChildBinding;
import com.google.gwt.inject.rebind.binding.ParentBinding;
import com.google.gwt.inject.rebind.resolution.ImplicitBindingCreator.BindingCreationException;
import com.google.inject.Key;
import junit.framework.TestCase;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.easymock.IMocksControl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class BindingResolverTest extends TestCase {

  private static final String SOURCE = "source";

  // List to record binding collections that we mock so that they don't get GC'd and have
  // false failures due to finalize being called
  private List<GinjectorBindings> nodes = new ArrayList<GinjectorBindings>();
  private List<Binding> bindings = new ArrayList<Binding>();

  private ImplicitBindingCreator bindingCreator;
  private ErrorManager errorManager;
  private ParentBinding parentBinding;
  private IMocksControl control;
  private BindingResolver bindingResolver;
  private TreeLogger treeLogger;

  private BindingFactory bindingFactory;

  private void replay() {
    control.replay();
  }
 
  private void verify() {
    EasyMock.verify(treeLogger);
    control.verify();
  }
 
  protected void setUp() throws Exception {
    super.setUp();

    treeLogger = EasyMock.createNiceMock("treeLogger", TreeLogger.class);
    // Ensure that branches get the same mock logger and not a null pointer.
    expect(treeLogger.branch(EasyMock.<TreeLogger.Type>anyObject(), EasyMock.<String>anyObject(),
        EasyMock.<Throwable>anyObject(), EasyMock.<TreeLogger.HelpInfo>anyObject()))
        .andReturn(treeLogger)
        .anyTimes();
    EasyMock.replay(treeLogger);

    control = createControl();
    parentBinding = control.createMock("parentBinding", ParentBinding.class);
    bindingCreator = control.createMock("bindingCreator", ImplicitBindingCreator.class);
    errorManager = control.createMock("errorManager", ErrorManager.class);
    bindingFactory = control.createMock("bindingFactory", BindingFactory.class);

    final ImplicitBindingCreator.Factory bindingCreatorFactory =
        new ImplicitBindingCreator.Factory() {
          @Override
          public ImplicitBindingCreator create(TreeLogger logger) {
            return bindingCreator;
          }
        };
    DependencyExplorer.Factory dependencyExplorerFactory =
        new DependencyExplorer.Factory() {
          @Override
          public DependencyExplorer create(TreeLogger logger) {
            return new DependencyExplorer(bindingCreatorFactory, logger);
          }
        };
    UnresolvedBindingValidator.Factory unresolvedBindingValidatorFactory =
        new UnresolvedBindingValidator.Factory() {
          @Override
          public UnresolvedBindingValidator create(TreeLogger logger) {
            return new UnresolvedBindingValidator(new EagerCycleFinder(errorManager), errorManager,
                logger);
          }
        };
    final BindingPositioner.Factory bindingPositionerFactory =
        new BindingPositioner.Factory() {
          @Override
          public BindingPositioner create(TreeLogger logger) {
            return new BindingPositioner(logger);
          }
        };
    BindingInstaller.Factory bindingInstallerFactory =
        new BindingInstaller.Factory() {
          @Override
          public BindingInstaller create(TreeLogger logger) {
            return new BindingInstaller(bindingPositionerFactory, bindingFactory, logger);
          }
        };

    bindingResolver = new BindingResolver(
        dependencyExplorerFactory,
        unresolvedBindingValidatorFactory,
        bindingInstallerFactory,
        treeLogger);
  }
 
  private GinjectorBindings createInjectorNode(String name) {
    GinjectorBindings node = control.createMock(name, GinjectorBindings.class);
    nodes.add(node);
    expect(node.getParent()).andStubReturn(null);
    expect(node.getChildren()).andStubReturn(new ArrayList<GinjectorBindings>());
    expect(node.isBound(isA(Key.class))).andStubReturn(false);
    expect(node.isBoundLocallyInChild(isA(Key.class))).andStubReturn(false);
    expect(node.getBinding(isA(Key.class))).andStubReturn(null);
    expect(node.getModuleName()).andStubReturn(name);
    expect(node.isPinned(isA(Key.class))).andStubReturn(false);
    return node;
  }
 
  // TODO(bchambers): This may be cleaner if we extract an interface for BindingResolver
  // to use when resolving bindings, and then create a "test" version of that.
  private void setChildren(GinjectorBindings parent, GinjectorBindings... children) {
    expect(parent.getChildren()).andReturn(Arrays.asList(children)).anyTimes();
    for (GinjectorBindings child : children) {
      expect(child.getParent()).andReturn(parent).anyTimes();
    }
  }
 
  // Creates the following hierarchy of binding collections and returns a
  // StandardTree object with names for all the nodes.
  //            root
  //           /    \
  //       childL  childR
  //       /    \
  // childLL    childLR
  public StandardTree createExampleTree() {
    StandardTree tree = new StandardTree();
    tree.root = createInjectorNode("root");
    tree.childL = createInjectorNode("childL");
    tree.childR = createInjectorNode("childR");
    tree.childLL = createInjectorNode("childLL");
    tree.childLR = createInjectorNode("childLR");
    setChildren(tree.root, tree.childL, tree.childR);
    setChildren(tree.childL, tree.childLL, tree.childLR);
    return tree;
  }
 
  private static class StandardTree {
    private GinjectorBindings root;
    private GinjectorBindings childL;
    private GinjectorBindings childLL;
    private GinjectorBindings childLR;
    private GinjectorBindings childR;
  }
 
  private void bind(Key<?> key, GinjectorBindings in) {
    Binding binding = control.createMock(Binding.class);
    expect(binding.getContext()).andReturn(Context.forText("")).anyTimes();
    expect(in.isBound(key)).andReturn(true).anyTimes();
    expect(in.getBinding(key)).andReturn(binding).anyTimes();
    bindings.add(binding);
  }
 
  private void bindChild(Key<?> key, GinjectorBindings parent, GinjectorBindings child) {
    ExposedChildBinding binding = control.createMock(ExposedChildBinding.class);
    expect(binding.getContext()).andReturn(Context.forText("")).anyTimes();
    expect(parent.isBound(key)).andReturn(true).anyTimes();
    expect(parent.getBinding(key)).andReturn(binding).anyTimes();
    expect(child.isPinned(key)).andReturn(true).anyTimes();
    expect(binding.getChildBindings()).andReturn(child).anyTimes();
    bindings.add(binding);
  }
 
  private void bindParent(Key<?> key, GinjectorBindings parent, GinjectorBindings child) {
    ParentBinding binding = control.createMock(ParentBinding.class);
    expect(binding.getContext()).andReturn(Context.forText("")).anyTimes();
    expect(binding.getParentBindings()).andReturn(parent).anyTimes();
    expect(child.isBound(key)).andReturn(true).anyTimes();
    expect(child.getBinding(key)).andReturn(binding).anyTimes();
    bindings.add(binding);
  }
 
  private void expectParentBinding(Key<?> key, GinjectorBindings parent, GinjectorBindings dest) {
    expect(bindingFactory.getParentBinding(eq(key), eq(parent), isA(Context.class)))
        .andReturn(parentBinding);
    dest.addBinding(key, parentBinding);
  }
 
  private void replayAndResolve(GinjectorBindings origin, Dependency... unresolved) {
    expect(origin.getDependencies()).andStubReturn(TestUtils.dependencyList(unresolved));
    replay();
    bindingResolver.resolveBindings(origin);
    verify();
  }
 
  public void testResolveFoundInRoot() {
    StandardTree tree = createExampleTree();
   
    bind(foo(), tree.root);
   
    expectParentBinding(foo(), tree.root, tree.childLL);

    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveDependenciesInRoot() throws Exception {
    StandardTree tree = createExampleTree();

    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()), required(foo(), baz()));
    bind(bar(), tree.root);
    bind(baz(), tree.root);
   
    tree.root.addBinding(foo(), fooBinding);
    expectParentBinding(foo(), tree.root, tree.childLL);
   
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveDependenciesInRoot_InheritedByChild() throws Exception {
    StandardTree tree = createExampleTree();
   
    // Bar already in root, inherited in childL.  Baz already in root.  Foo can still be in root.
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()), required(foo(), baz()));
    bind(bar(), tree.root);
    bindParent(bar(), tree.root, tree.childL);
    bind(baz(), tree.root);
   

    tree.root.addBinding(foo(), fooBinding);
    expectParentBinding(foo(), tree.root, tree.childLL);
   
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveDependenciesInOriginExposedToParent() throws Exception {
    StandardTree tree = createExampleTree();
    // Bar is bound in the child, but exposed to the root.  Foo should still be in root
    bind(bar(), tree.childR);
    bindChild(bar(), tree.root, tree.childR);
    bind(baz(), tree.root);
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()), required(foo(), baz()));

    tree.root.addBinding(foo(), fooBinding);
    expectParentBinding(foo(), tree.root, tree.childR);

    replayAndResolve(tree.childR, required(Dependency.GINJECTOR, foo()));
  }

  public void testResolveDependenciesResolveInOriginExposedToParent() throws Exception {
    StandardTree tree = createExampleTree();
    // Bar is bound in the child, but exposed to the root.  Foo should still be in root
    bindChild(bar(), tree.root, tree.childR);
    bind(baz(), tree.root);
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()), required(foo(), baz()));
    Binding barBinding = expectCreateBinding(bar());

    tree.root.addBinding(foo(), fooBinding);
    tree.childR.addBinding(bar(), barBinding);
    expectParentBinding(foo(), tree.root, tree.childR);

    replayAndResolve(tree.childR, required(Dependency.GINJECTOR, foo()));
  }

  public void testResolveDependenciesInChildL_ExposedToRoot() throws Exception {
    StandardTree tree = createExampleTree();
    // Bar is bound in the child, but exposed to the root.  Foo should still be in root
    bind(bar(), tree.childL);
    bindChild(bar(), tree.root, tree.childL);
    bind(baz(), tree.root);
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()), required(foo(), baz()));

    tree.root.addBinding(foo(), fooBinding);
    expectParentBinding(foo(), tree.root, tree.childLL);
       
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveOneDependencyInChildL() throws Exception {
    StandardTree tree = createExampleTree();
    bind(bar(), tree.root);
    bind(baz(), tree.childL);
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()), required(foo(), baz()));
   
    expectParentBinding(bar(), tree.root, tree.childL); // childL gets Bar from root
    tree.childL.addBinding(foo(), fooBinding);
    expectParentBinding(foo(), tree.childL, tree.childLL); // childLL gets foo from childL
       
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveOptionalKey_Fails() throws Exception {
    StandardTree tree = createExampleTree();
    expect(bindingCreator.create(foo())).andThrow(new BindingCreationException("Unable to create"));
       
    replayAndResolve(tree.childLL, optional(Dependency.GINJECTOR, foo()));
    // No error because foo() is optional.
  }
 
  public void testResolveBindingWithOptionalDependency_DepFails() throws Exception{
    StandardTree tree = createExampleTree();
    // Baz is optional and fails to resolve
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()), optional(foo(), baz()));
    expect(bindingCreator.create(baz())).andThrow(new BindingCreationException("Unable to create"));
    bind(bar(), tree.root);

    tree.root.addBinding(foo(), fooBinding);
    expectParentBinding(foo(), tree.root, tree.childLL); // childLL gets foo from childL
       
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  // Tries to create Foo, which has an optional dependency on Bar, which requires Baz.
  // Baz can't be created, so it should create Foo, without the Bar.
  public void testResolveBindingWithOptionalDependencyThatFails() throws Exception {   
    StandardTree tree = createExampleTree();
   
    Binding fooBinding = expectCreateBinding(foo(), optional(foo(), bar()));
    expectCreateBinding(bar(), required(bar(), baz()));
    expect(bindingCreator.create(baz())).andThrow(new BindingCreationException("Unable to create"));
    tree.root.addBinding(foo(), fooBinding);
    expectParentBinding(foo(), tree.root, tree.childLL);
       
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveBindingWithOptionalThatDoesntBlockPosition() throws Exception {
    StandardTree tree = createExampleTree();
    Binding fooBinding = expectCreateBinding(foo(), optional(foo(), bar()));
    expectCreateBinding(bar(), required(bar(), baz()));
    expectCreateBinding(baz());
   
    // Can't bar() because baz() is already bound in childLL.  Therefore, bar() should not constrain
    // the position of foo(), and we should place it in the root.
    bind(baz(), tree.childLL);
    expect(tree.childL.isBoundLocallyInChild(baz())).andReturn(true);
    expect(tree.childL.getChildWhichBindsLocally(baz())).andReturn(tree.childLL);

    tree.root.addBinding(foo(), fooBinding);
    expectParentBinding(foo(), tree.root, tree.childL);
    replayAndResolve(tree.childL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testFailToCreateImplicitBinding() throws Exception {
    StandardTree tree = createExampleTree();
    expect(bindingCreator.create(foo())).andThrow(new BindingCreationException("Unable to create"));
   
    errorManager.logError(isA(String.class), eq(foo()), isA(String.class), isA(List.class));
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testFailToResolveDependency() throws Exception {
    StandardTree tree = createExampleTree();
    expectCreateBinding(foo(), required(foo(), bar()));
   
    expect(bindingCreator.create(bar()))
        .andThrow(new BindingCreationException("Unable to create"));
       
    errorManager.logError(isA(String.class), eq(bar()), isA(String.class), isA(List.class));
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testCircularDependency() throws Exception {
    GinjectorBindings root = createInjectorNode("root");
   
    expectCreateBinding(bar(), required(bar(), baz()));
    expectCreateBinding(baz(), required(baz(), bar()));
   
    Capture<String> errorMessage = new Capture<String>();
    errorManager.logError(isA(String.class), isA(Object.class), isA(Object.class));

    // Intentionally use a different key, so that == won't work
    replayAndResolve(root, required(Dependency.GINJECTOR, bar()));
  }
 
  public void testCycleDetectionForBindFooToFooImpl() throws Exception {
    GinjectorBindings root = createInjectorNode("root");
   
    bind(foo(), root);
    expectCreateBinding(fooImpl(), required(fooImpl(), bar()));
    expectCreateBinding(bar(), required(bar(), foo()));
   
    Capture<String> errorMessage = new Capture<String>();
    errorManager.logError(EasyMock.isA(String.class), EasyMock.anyObject(), EasyMock.anyObject());

    // Intentionally use a different key, so that == won't work
    replayAndResolve(root, required(foo(), fooImpl()));
  }
 
  public void testOneNode() throws Exception {
    GinjectorBindings root = createInjectorNode("root");
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()), required(foo(), baz()));
    bind(bar(), root);
    bind(baz(), root);
    root.addBinding(foo(), fooBinding);
       
    replayAndResolve(root, required(Dependency.GINJECTOR, foo()));
  }
  public void testDependencyInOtherChild() throws Exception {
    // Test one of the "weird" behaviors in Guice. Foo depends on Bar and Baz.  Because
    // Bar is bound in a sibling, we can't create Bar in the parent.  Therefore,
    // we create Bar (and Foo) in the origin
    GinjectorBindings root = createInjectorNode("root");
    GinjectorBindings childL = createInjectorNode("childL");
    GinjectorBindings childR = createInjectorNode("childR");
    setChildren(root, childL, childR);
   
    bind(baz(), root);
    bind(bar(), childL);
    expect(root.isBoundLocallyInChild(bar())).andReturn(true).anyTimes();
   
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()), required(foo(), baz()));
    Binding barBinding = expectCreateBinding(bar());
   
    childR.addBinding(bar(), barBinding);
    expectParentBinding(baz(), root, childR);
    childR.addBinding(foo(), fooBinding);
       
    replayAndResolve(childR, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testDepHiddenInChildBlocksResolvingInRoot() throws Exception {
    GinjectorBindings root = createInjectorNode("root");
    GinjectorBindings child = createInjectorNode("child_module");
    setChildren(root, child);
   
    bind(baz(), root);
    bind(bar(), child);
    expect(root.isBoundLocallyInChild(bar())).andReturn(true).anyTimes();
    expect(root.getChildWhichBindsLocally(bar())).andReturn(child);
   
    expectCreateBinding(foo(), required(foo(), bar()), required(foo(), baz()));
    expectCreateBinding(bar());
    Capture<String> errorMessage = new Capture<String>();
    errorManager.logError(isA(String.class), isA(Object.class), capture(errorMessage),
        isA(Object.class)); // failure to create bar b/c already bound

    replayAndResolve(root, required(Dependency.GINJECTOR, foo()));

    assertTrue(errorMessage.getValue().contains("child_module"));
  }
 
  public void testDepHiddenInChildBlocksResolvingInRoot_NoErrorIfOptional() throws Exception {
    GinjectorBindings root = createInjectorNode("root");
    GinjectorBindings child = createInjectorNode("child");
    setChildren(root, child);
   
    bind(baz(), root);
    bind(bar(), child);
    expect(root.isBoundLocallyInChild(bar())).andReturn(true).anyTimes();
    expect(root.getChildWhichBindsLocally(bar())).andReturn(child);
   
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), baz()), optional(foo(), bar()));
    expectCreateBinding(bar());

    root.addBinding(foo(), fooBinding);
       
    replayAndResolve(root, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testManyChildren() throws Exception {
    GinjectorBindings root = createInjectorNode("root");
    GinjectorBindings child1 = createInjectorNode("child1");
    GinjectorBindings child2 = createInjectorNode("child2");
    GinjectorBindings child3 = createInjectorNode("child3");
    setChildren(root, child1, child2, child3);
   
    bind(bar(), child1);
    bindChild(bar(), root, child1);
    bind(baz(), child2);
    bindChild(baz(), root, child2);
   
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()), required(foo(), baz()));
    root.addBinding(foo(), fooBinding);
    expectParentBinding(foo(), root, child3);
       
    replayAndResolve(child3, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveCycleThroughProvider() throws Exception {
    // Foo -> Bar ->Provider<Foo> -> Foo, cycle is OK because of Provider. 
    // Provider<Foo> is in the "unpositioned pending Foo" set.
    StandardTree tree = createExampleTree();
   
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()));
    Binding barBinding = expectCreateBinding(bar(), required(bar(), providerFoo()));
    Binding providerFooBinding = expectCreateBinding(providerFoo(),
        requiredLazy(providerFoo(), foo()));
   
    tree.root.addBinding(foo(), fooBinding);
    tree.root.addBinding(bar(), barBinding);
    tree.root.addBinding(providerFoo(), providerFooBinding);
    expectParentBinding(foo(), tree.root, tree.childLL);
       
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveCycleThroughAsyncProvider() throws Exception {
    // Foo -> AsyncProvider<Foo> -> Foo, cycle is OK because of AsyncProvider. 
    // AsyncProvider<Foo> is in the "unpositioned pending Foo" set.  Identical to
    // testResolveCycleThroughProvider, but verifies that AsyncProvider is also acceptable.
    StandardTree tree = createExampleTree();
   
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), asyncProviderFoo()));
    Binding providerFooBinding = expectCreateBinding(
        asyncProviderFoo(), requiredLazy(asyncProviderFoo(), foo()));
   
    tree.root.addBinding(foo(), fooBinding);
    tree.root.addBinding(asyncProviderFoo(), providerFooBinding);
    expectParentBinding(foo(), tree.root, tree.childLL);
       
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveCycleDepOfProviderBound() throws Exception {
    // Foo -> Provider<Bar> -> Bar -> {Foo, Baz}, Baz is bound at childL
    // This test makes sure that we at least ensure that Bar doesn't move higher than
    // *any* of it's dependencies, even after detecting a cycle.
    StandardTree tree = createExampleTree();
   
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), providerBar()));
    Binding providerBarBinding = expectCreateBinding(providerBar(),
        requiredLazy(providerBar(), bar()));
    Binding barBinding = expectCreateBinding(bar(), required(bar(), foo()), required(bar(), baz()));
    bind(baz(), tree.childL);
   
    tree.childL.addBinding(foo(), fooBinding);
    tree.childL.addBinding(providerBar(), providerBarBinding);
    tree.childL.addBinding(bar(), barBinding);
    expectParentBinding(foo(), tree.childL, tree.childLL);
       
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveCycleDepBeforeProviderBound() throws Exception {
    // Foo -> {Baz, Provider<Bar>}; Provider<Bar> -> Bar -> Foo. Baz is bound at childL
    // Similar to the last one, although this time the already bound dependency comes before
    // earlier in the cycle (Before the provider).  We should still make sure that Foo is
    // in the correct position.
    StandardTree tree = createExampleTree();
   
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), baz()),
        required(foo(), providerBar()));
    Binding providerBarBinding = expectCreateBinding(providerBar(),
        requiredLazy(providerBar(), bar()));
    Binding barBinding = expectCreateBinding(bar(), required(bar(), foo()));
    bind(baz(), tree.childL);
   
    tree.childL.addBinding(foo(), fooBinding);
    tree.childL.addBinding(providerBar(), providerBarBinding);
    tree.childL.addBinding(bar(), barBinding);
    expectParentBinding(foo(), tree.childL, tree.childLL);
       
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveIndirectCycleThroughProvider() throws Exception {
    // Foo -> Baz -> Provider<Bar> -> Bar -> Foo
    StandardTree tree = createExampleTree();
   
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), baz()));
    Binding bazBinding = expectCreateBinding(baz(), required(baz(), providerBar()));
    Binding providerBarBinding = expectCreateBinding(
        providerBar(), requiredLazy(providerBar(), bar()));
    Binding barBinding = expectCreateBinding(bar(), required(bar(), foo()));
   
    tree.root.addBinding(foo(), fooBinding);
    tree.root.addBinding(providerBar(), providerBarBinding);
    tree.root.addBinding(bar(), barBinding);
    tree.root.addBinding(baz(), bazBinding);
    expectParentBinding(foo(), tree.root, tree.childLL);
       
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
 
  public void testResolveMultipleCycles() throws Exception {
    // Foo -> Bar -> Provider<Baz> -> Baz -> {Provider<Foo>, Provider<Bar>, Provider<Baz>}
    // Resolves multiple cycles, including a cycle that starts with a Provider
    StandardTree tree = createExampleTree();
   
    Binding fooBinding = expectCreateBinding(foo(), required(foo(), bar()));
    Binding barBinding = expectCreateBinding(bar(), required(bar(), providerBaz()));
    Binding providerBazBinding = expectCreateBinding(
        providerBaz(), requiredLazy(providerBaz(), baz()));
    Binding bazBinding = expectCreateBinding(baz(), required(baz(), providerFoo()),
        required(baz(), providerFoo()), required(baz(), providerBar()));
    Binding providerFooBinding = expectCreateBinding(
        providerFoo(), requiredLazy(providerFoo(), foo()));
    Binding providerBarBinding = expectCreateBinding(
        providerBar(), requiredLazy(providerBar(), bar()));

    tree.root.addBinding(foo(), fooBinding);
    tree.root.addBinding(providerBar(), providerBarBinding);
    tree.root.addBinding(providerFoo(), providerFooBinding);
    tree.root.addBinding(providerBaz(), providerBazBinding);
    tree.root.addBinding(bar(), barBinding);
    tree.root.addBinding(baz(), bazBinding);
    expectParentBinding(foo(), tree.root, tree.childLL);
       
    replayAndResolve(tree.childLL, required(Dependency.GINJECTOR, foo()));
  }
   
  private Dependency required(Key<?> source, Key<?> key) {
    return new Dependency(source, key, SOURCE);
  }
 
  private Dependency optional(Key<?> source, Key<?> key) {
    return new Dependency(source, key, true, false, SOURCE);
  }
 
  private Dependency requiredLazy(Key<?> source, Key<?> key) {
    return new Dependency(source, key, false, true, SOURCE);
  }
 
  private Binding expectCreateBinding(Key<?> key, Dependency... keys) throws Exception {
    Binding binding = control.createMock(Binding.class);
    expect(bindingCreator.create(key)).andReturn(binding);
    Set<Dependency> requiredKeys = new HashSet<Dependency>(keys.length);
    Collections.addAll(requiredKeys, keys);
    expect(binding.getDependencies()).andReturn(requiredKeys).atLeastOnce();
    bindings.add(binding);
    return binding;
  }
}
TOP

Related Classes of com.google.gwt.inject.rebind.resolution.BindingResolverTest$StandardTree

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.