/** * 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(); } }