/**
* Copyright 2011-2015 John Ericksen
*
* 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 org.androidtransfuse.analysis;
import com.sun.codemodel.JClassAlreadyExistsException;
import org.androidtransfuse.adapter.ASTType;
import org.androidtransfuse.adapter.PackageClass;
import org.androidtransfuse.adapter.classes.ASTClassFactory;
import org.androidtransfuse.analysis.astAnalyzer.ASTInjectionAspect;
import org.androidtransfuse.analysis.astAnalyzer.VirtualProxyAspect;
import org.androidtransfuse.analysis.targets.*;
import org.androidtransfuse.bootstrap.Bootstrap;
import org.androidtransfuse.bootstrap.Bootstraps;
import org.androidtransfuse.gen.CodeGenerationUtil;
import org.androidtransfuse.gen.InjectionFragmentGeneratorHarness;
import org.androidtransfuse.gen.variableBuilder.ProviderVariableBuilder;
import org.androidtransfuse.gen.variableBuilder.VariableBuilder;
import org.androidtransfuse.gen.variableBuilder.VariableInjectionBuilder;
import org.androidtransfuse.gen.variableBuilder.VariableInjectionBuilderFactory;
import org.androidtransfuse.model.ConstructorInjectionPoint;
import org.androidtransfuse.model.InjectionNode;
import org.junit.Before;
import org.junit.Test;
import javax.inject.Inject;
import javax.inject.Provider;
import java.io.IOException;
import static org.junit.Assert.*;
/**
* @author John Ericksen
*/
@Bootstrap
public class LoopAnalysisTest {
@Inject
private Analyzer analyzer;
@Inject
private ASTClassFactory astClassFactory;
@Inject
private SimpleAnalysisContextFactory analysisContextFactory;
private AnalysisContext analysisContext;
@Inject
private VariableInjectionBuilder variableInjectionBuilder;
@Inject
private InjectionFragmentGeneratorHarness fragmentGeneratorHarness;
@Inject
private CodeGenerationUtil codeGenerationUtil;
@Inject
private VariableInjectionBuilderFactory variableInjectionBuilderFactory;
//A -> B (BImpl) -> C -> A
//D -> E (EImpl) -> F (FProvider.get()) -> D
@Before
public void setup() {
Bootstraps.inject(this);
A.reset();
BImpl.reset();
C.reset();
analysisContext = analysisContextFactory.buildContext();
analysisContext.getInjectionNodeBuilders().putType(B.class,
variableInjectionBuilderFactory.buildVariableInjectionNodeBuilder(astClassFactory.getType(BImpl.class)));
analysisContext.getInjectionNodeBuilders().putType(F.class,
variableInjectionBuilderFactory.buildProviderInjectionNodeBuilder(astClassFactory.getType(FProvider.class)));
analysisContext.getInjectionNodeBuilders().putType(E.class,
variableInjectionBuilderFactory.buildVariableInjectionNodeBuilder(astClassFactory.getType(EImpl.class)));
}
@Test
public void testProviderLoop() {
ASTType astType = astClassFactory.getType(D.class);
InjectionNode injectionNode = analyzer.analyze(astType, astType, analysisContext);
injectionNode.addAspect(VariableBuilder.class, variableInjectionBuilder);
//D -> E
ConstructorInjectionPoint deConstructorInjectionPoint = injectionNode.getAspect(ASTInjectionAspect.class).getConstructorInjectionPoint();
assertEquals(1, deConstructorInjectionPoint.getInjectionNodes().size());
InjectionNode eInjectionNode = deConstructorInjectionPoint.getInjectionNodes().get(0);
assertTrue(isProxyRequired(eInjectionNode));
assertEquals(EImpl.class.getCanonicalName(), eInjectionNode.getClassName());
//E -> F
ConstructorInjectionPoint efConstructorInjectionPoint = eInjectionNode.getAspect(ASTInjectionAspect.class).getConstructorInjectionPoint();
assertEquals(1, efConstructorInjectionPoint.getInjectionNodes().size());
InjectionNode fInjectionNode = efConstructorInjectionPoint.getInjectionNodes().get(0);
assertFalse(isProxyRequired(fInjectionNode));
assertEquals(F.class.getCanonicalName(), fInjectionNode.getClassName());
//F -> D
InjectionNode fProviderInjectionNode = ((ProviderVariableBuilder) fInjectionNode.getAspect(VariableBuilder.class)).getProviderInjectionNode();
assertFalse(isProxyRequired(fProviderInjectionNode));
assertEquals(FProvider.class.getCanonicalName(), fProviderInjectionNode.getClassName());
}
@Test
public void testLoopGeneration() throws JClassAlreadyExistsException, ClassNotFoundException, IOException, IllegalAccessException, InstantiationException {
ASTType astType = astClassFactory.getType(C.class);
InjectionNode injectionNode = analyzer.analyze(astType, astType, analysisContext);
PackageClass providerPC = new PackageClass("org.androidtransfuse", "TestProvider_Example1");
fragmentGeneratorHarness.buildProvider(injectionNode, providerPC);
ClassLoader classLoader = codeGenerationUtil.build();
Class<Provider<C>> providerClass = (Class<Provider<C>>) classLoader.loadClass(providerPC.getCanonicalName());
Provider<C> testProvider = providerClass.newInstance();
C c = testProvider.get();
assertEquals(2, BImpl.getConstructionCounter());
//only one C because we have already created a C in the injection stack parents
assertEquals(1, C.getConstructionCounter());
assertEquals(1, A.getConstructionCounter());
assertEquals(c, c.getA().getB().getC());
}
@Test
public void testLoopRootGeneration() throws JClassAlreadyExistsException, ClassNotFoundException, IOException, IllegalAccessException, InstantiationException {
ASTType astType = astClassFactory.getType(B.class);
ASTType astImplType = astClassFactory.getType(BImpl.class);
InjectionNode injectionNode = analyzer.analyze(astType, astImplType, analysisContext);
PackageClass providerPC = new PackageClass("org.androidtransfuse", "TestProvider_Example2");
fragmentGeneratorHarness.buildProvider(injectionNode, providerPC);
ClassLoader classLoader = codeGenerationUtil.build();
Class<Provider<B>> providerClass = (Class<Provider<B>>) classLoader.loadClass(providerPC.getCanonicalName());
Provider<B> testProvider = providerClass.newInstance();
B b = testProvider.get();
assertEquals(1, BImpl.getConstructionCounter());
//only one C because we have already created a C in the injection stack parents
assertEquals(1, C.getConstructionCounter());
assertEquals(1, A.getConstructionCounter());
assertEquals(b, b.getC().getA().getB());
}
@Test
public void testLoopWithPostDependenciesGeneration() throws JClassAlreadyExistsException, ClassNotFoundException, IOException, IllegalAccessException, InstantiationException {
ASTType astType = astClassFactory.getType(A.class);
InjectionNode injectionNode = analyzer.analyze(astType, astType, analysisContext);
PackageClass providerPC = new PackageClass("org.androidtransfuse", "TestProvider_Example3");
fragmentGeneratorHarness.buildProvider(injectionNode, providerPC);
ClassLoader classLoader = codeGenerationUtil.build();
Class<Provider<A>> providerClass = (Class<Provider<A>>) classLoader.loadClass(providerPC.getCanonicalName());
Provider<A> testProvider = providerClass.newInstance();
A a = testProvider.get();
assertEquals(2, BImpl.getConstructionCounter());
assertEquals(2, C.getConstructionCounter());
assertEquals(1, A.getConstructionCounter());
assertEquals(a, a.getB().getC().getA());
}
@Test
public void testBackLinkAnalysis() {
ASTType astType = astClassFactory.getType(A.class);
InjectionNode injectionNode = analyzer.analyze(astType, astType, analysisContext);
injectionNode.addAspect(VariableBuilder.class, variableInjectionBuilder);
//A -> B
ConstructorInjectionPoint abConstructorInjectionPoint = injectionNode.getAspect(ASTInjectionAspect.class).getConstructorInjectionPoint();
assertEquals(1, abConstructorInjectionPoint.getInjectionNodes().size());
InjectionNode bInjectionNode = abConstructorInjectionPoint.getInjectionNodes().get(0);
assertTrue(isProxyRequired(bInjectionNode));
assertEquals(BImpl.class.getCanonicalName(), bInjectionNode.getClassName());
//B -> C
ConstructorInjectionPoint bcConstructorInjectionPoint = bInjectionNode.getAspect(ASTInjectionAspect.class).getConstructorInjectionPoint();
assertEquals(1, bcConstructorInjectionPoint.getInjectionNodes().size());
InjectionNode cInjectionNode = bcConstructorInjectionPoint.getInjectionNodes().get(0);
assertFalse(isProxyRequired(cInjectionNode));
assertEquals(C.class.getCanonicalName(), cInjectionNode.getClassName());
//C -> A
ConstructorInjectionPoint caConstructorInjectionPoint = cInjectionNode.getAspect(ASTInjectionAspect.class).getConstructorInjectionPoint();
assertEquals(1, caConstructorInjectionPoint.getInjectionNodes().size());
InjectionNode aInjectionNode = caConstructorInjectionPoint.getInjectionNodes().get(0);
assertFalse(isProxyRequired(aInjectionNode));
assertEquals(A.class.getCanonicalName(), aInjectionNode.getClassName());
assertEquals(injectionNode, aInjectionNode);
}
private boolean isProxyRequired(InjectionNode injectionNode) {
VirtualProxyAspect proxyAspect = injectionNode.getAspect(VirtualProxyAspect.class);
return proxyAspect != null && proxyAspect.isProxyRequired();
}
}