1*f585d8a3SJacky Wang /* 2*f585d8a3SJacky Wang * Copyright (C) 2016 The Dagger Authors. 3*f585d8a3SJacky Wang * 4*f585d8a3SJacky Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5*f585d8a3SJacky Wang * you may not use this file except in compliance with the License. 6*f585d8a3SJacky Wang * You may obtain a copy of the License at 7*f585d8a3SJacky Wang * 8*f585d8a3SJacky Wang * http://www.apache.org/licenses/LICENSE-2.0 9*f585d8a3SJacky Wang * 10*f585d8a3SJacky Wang * Unless required by applicable law or agreed to in writing, software 11*f585d8a3SJacky Wang * distributed under the License is distributed on an "AS IS" BASIS, 12*f585d8a3SJacky Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*f585d8a3SJacky Wang * See the License for the specific language governing permissions and 14*f585d8a3SJacky Wang * limitations under the License. 15*f585d8a3SJacky Wang */ 16*f585d8a3SJacky Wang 17*f585d8a3SJacky Wang package dagger.internal.codegen; 18*f585d8a3SJacky Wang 19*f585d8a3SJacky Wang import static com.google.common.truth.Truth.assertAbout; 20*f585d8a3SJacky Wang 21*f585d8a3SJacky Wang import androidx.room.compiler.processing.util.Source; 22*f585d8a3SJacky Wang import com.google.common.collect.ImmutableList; 23*f585d8a3SJacky Wang import com.google.common.truth.FailureMetadata; 24*f585d8a3SJacky Wang import com.google.common.truth.Subject; 25*f585d8a3SJacky Wang import com.google.common.truth.Truth; 26*f585d8a3SJacky Wang import dagger.Module; 27*f585d8a3SJacky Wang import dagger.producers.ProducerModule; 28*f585d8a3SJacky Wang import dagger.testing.compile.CompilerTests; 29*f585d8a3SJacky Wang import java.io.PrintWriter; 30*f585d8a3SJacky Wang import java.io.StringWriter; 31*f585d8a3SJacky Wang import java.util.Arrays; 32*f585d8a3SJacky Wang import java.util.List; 33*f585d8a3SJacky Wang 34*f585d8a3SJacky Wang /** A {@link Truth} subject for testing Dagger module methods. */ 35*f585d8a3SJacky Wang final class DaggerModuleMethodSubject extends Subject { 36*f585d8a3SJacky Wang 37*f585d8a3SJacky Wang /** A {@link Truth} subject factory for testing Dagger module methods. */ 38*f585d8a3SJacky Wang static final class Factory implements Subject.Factory<DaggerModuleMethodSubject, String> { 39*f585d8a3SJacky Wang 40*f585d8a3SJacky Wang /** Starts a clause testing a Dagger {@link Module @Module} method. */ assertThatModuleMethod(String method)41*f585d8a3SJacky Wang static DaggerModuleMethodSubject assertThatModuleMethod(String method) { 42*f585d8a3SJacky Wang return assertAbout(new Factory()) 43*f585d8a3SJacky Wang .that(method) 44*f585d8a3SJacky Wang .withDeclaration("@Module abstract class %s { %s }"); 45*f585d8a3SJacky Wang } 46*f585d8a3SJacky Wang 47*f585d8a3SJacky Wang /** Starts a clause testing a Dagger {@link ProducerModule @ProducerModule} method. */ assertThatProductionModuleMethod(String method)48*f585d8a3SJacky Wang static DaggerModuleMethodSubject assertThatProductionModuleMethod(String method) { 49*f585d8a3SJacky Wang return assertAbout(new Factory()) 50*f585d8a3SJacky Wang .that(method) 51*f585d8a3SJacky Wang .withDeclaration("@ProducerModule abstract class %s { %s }"); 52*f585d8a3SJacky Wang } 53*f585d8a3SJacky Wang 54*f585d8a3SJacky Wang /** Starts a clause testing a method in an unannotated class. */ assertThatMethodInUnannotatedClass(String method)55*f585d8a3SJacky Wang static DaggerModuleMethodSubject assertThatMethodInUnannotatedClass(String method) { 56*f585d8a3SJacky Wang return assertAbout(new Factory()) 57*f585d8a3SJacky Wang .that(method) 58*f585d8a3SJacky Wang .withDeclaration("abstract class %s { %s }"); 59*f585d8a3SJacky Wang } 60*f585d8a3SJacky Wang Factory()61*f585d8a3SJacky Wang private Factory() {} 62*f585d8a3SJacky Wang 63*f585d8a3SJacky Wang @Override createSubject(FailureMetadata failureMetadata, String that)64*f585d8a3SJacky Wang public DaggerModuleMethodSubject createSubject(FailureMetadata failureMetadata, String that) { 65*f585d8a3SJacky Wang return new DaggerModuleMethodSubject(failureMetadata, that); 66*f585d8a3SJacky Wang } 67*f585d8a3SJacky Wang } 68*f585d8a3SJacky Wang 69*f585d8a3SJacky Wang private final String actual; 70*f585d8a3SJacky Wang private final ImmutableList.Builder<String> imports = 71*f585d8a3SJacky Wang new ImmutableList.Builder<String>() 72*f585d8a3SJacky Wang .add( 73*f585d8a3SJacky Wang // explicitly import Module so it's not ambiguous with java.lang.Module 74*f585d8a3SJacky Wang "import dagger.Module;", 75*f585d8a3SJacky Wang "import dagger.*;", 76*f585d8a3SJacky Wang "import dagger.multibindings.*;", 77*f585d8a3SJacky Wang "import dagger.producers.*;", 78*f585d8a3SJacky Wang "import java.util.*;", 79*f585d8a3SJacky Wang "import javax.inject.*;"); 80*f585d8a3SJacky Wang private String declaration; 81*f585d8a3SJacky Wang private ImmutableList<Source> additionalSources = ImmutableList.of(); 82*f585d8a3SJacky Wang DaggerModuleMethodSubject(FailureMetadata failureMetadata, String subject)83*f585d8a3SJacky Wang private DaggerModuleMethodSubject(FailureMetadata failureMetadata, String subject) { 84*f585d8a3SJacky Wang super(failureMetadata, subject); 85*f585d8a3SJacky Wang this.actual = subject; 86*f585d8a3SJacky Wang } 87*f585d8a3SJacky Wang 88*f585d8a3SJacky Wang /** 89*f585d8a3SJacky Wang * Imports classes and interfaces. Note that all types in the following packages are already 90*f585d8a3SJacky Wang * imported:<ul> 91*f585d8a3SJacky Wang * <li>{@code dagger.*} 92*f585d8a3SJacky Wang * <li>{@code dagger.multibindings.*} 93*f585d8a3SJacky Wang * <li>(@code dagger.producers.*} 94*f585d8a3SJacky Wang * <li>{@code java.util.*} 95*f585d8a3SJacky Wang * <li>{@code javax.inject.*} 96*f585d8a3SJacky Wang * </ul> 97*f585d8a3SJacky Wang */ importing(Class<?>.... imports)98*f585d8a3SJacky Wang DaggerModuleMethodSubject importing(Class<?>... imports) { 99*f585d8a3SJacky Wang return importing(Arrays.asList(imports)); 100*f585d8a3SJacky Wang } 101*f585d8a3SJacky Wang 102*f585d8a3SJacky Wang /** 103*f585d8a3SJacky Wang * Imports classes and interfaces. Note that all types in the following packages are already 104*f585d8a3SJacky Wang * imported:<ul> 105*f585d8a3SJacky Wang * <li>{@code dagger.*} 106*f585d8a3SJacky Wang * <li>{@code dagger.multibindings.*} 107*f585d8a3SJacky Wang * <li>(@code dagger.producers.*} 108*f585d8a3SJacky Wang * <li>{@code java.util.*} 109*f585d8a3SJacky Wang * <li>{@code javax.inject.*} 110*f585d8a3SJacky Wang * </ul> 111*f585d8a3SJacky Wang */ importing(List<? extends Class<?>> imports)112*f585d8a3SJacky Wang DaggerModuleMethodSubject importing(List<? extends Class<?>> imports) { 113*f585d8a3SJacky Wang imports.stream() 114*f585d8a3SJacky Wang .map(clazz -> String.format("import %s;", clazz.getCanonicalName())) 115*f585d8a3SJacky Wang .forEachOrdered(this.imports::add); 116*f585d8a3SJacky Wang return this; 117*f585d8a3SJacky Wang } 118*f585d8a3SJacky Wang 119*f585d8a3SJacky Wang /** 120*f585d8a3SJacky Wang * Sets the declaration of the module. Must be a string with two {@code %s} parameters. The first 121*f585d8a3SJacky Wang * will be replaced with the name of the type, and the second with the method declaration, which 122*f585d8a3SJacky Wang * must be within paired braces. 123*f585d8a3SJacky Wang */ withDeclaration(String declaration)124*f585d8a3SJacky Wang DaggerModuleMethodSubject withDeclaration(String declaration) { 125*f585d8a3SJacky Wang this.declaration = declaration; 126*f585d8a3SJacky Wang return this; 127*f585d8a3SJacky Wang } 128*f585d8a3SJacky Wang 129*f585d8a3SJacky Wang /** Additional source files that must be compiled with the module. */ withAdditionalSources(Source... sources)130*f585d8a3SJacky Wang DaggerModuleMethodSubject withAdditionalSources(Source... sources) { 131*f585d8a3SJacky Wang this.additionalSources = ImmutableList.copyOf(sources); 132*f585d8a3SJacky Wang return this; 133*f585d8a3SJacky Wang } 134*f585d8a3SJacky Wang 135*f585d8a3SJacky Wang /** 136*f585d8a3SJacky Wang * Fails if compiling the module with the method doesn't report an error at the method 137*f585d8a3SJacky Wang * declaration whose message contains {@code errorSubstring}. 138*f585d8a3SJacky Wang */ hasError(String errorSubstring)139*f585d8a3SJacky Wang void hasError(String errorSubstring) { 140*f585d8a3SJacky Wang String source = moduleSource(); 141*f585d8a3SJacky Wang Source module = CompilerTests.javaSource("test.TestModule", source); 142*f585d8a3SJacky Wang CompilerTests.daggerCompiler( 143*f585d8a3SJacky Wang ImmutableList.<Source>builder().add(module).addAll(additionalSources).build()) 144*f585d8a3SJacky Wang .compile( 145*f585d8a3SJacky Wang subject -> 146*f585d8a3SJacky Wang subject 147*f585d8a3SJacky Wang .hasErrorContaining(errorSubstring) 148*f585d8a3SJacky Wang .onSource(module) 149*f585d8a3SJacky Wang .onLine(methodLine(source))); 150*f585d8a3SJacky Wang } 151*f585d8a3SJacky Wang methodLine(String source)152*f585d8a3SJacky Wang private int methodLine(String source) { 153*f585d8a3SJacky Wang String beforeMethod = source.substring(0, source.indexOf(actual)); 154*f585d8a3SJacky Wang int methodLine = 1; 155*f585d8a3SJacky Wang for (int nextNewlineIndex = beforeMethod.indexOf('\n'); 156*f585d8a3SJacky Wang nextNewlineIndex >= 0; 157*f585d8a3SJacky Wang nextNewlineIndex = beforeMethod.indexOf('\n', nextNewlineIndex + 1)) { 158*f585d8a3SJacky Wang methodLine++; 159*f585d8a3SJacky Wang } 160*f585d8a3SJacky Wang return methodLine; 161*f585d8a3SJacky Wang } 162*f585d8a3SJacky Wang moduleSource()163*f585d8a3SJacky Wang private String moduleSource() { 164*f585d8a3SJacky Wang StringWriter stringWriter = new StringWriter(); 165*f585d8a3SJacky Wang PrintWriter writer = new PrintWriter(stringWriter); 166*f585d8a3SJacky Wang writer.println("package test;"); 167*f585d8a3SJacky Wang writer.println(); 168*f585d8a3SJacky Wang for (String importLine : imports.build()) { 169*f585d8a3SJacky Wang writer.println(importLine); 170*f585d8a3SJacky Wang } 171*f585d8a3SJacky Wang writer.println(); 172*f585d8a3SJacky Wang writer.printf(declaration, "TestModule", "\n" + actual + "\n"); 173*f585d8a3SJacky Wang writer.println(); 174*f585d8a3SJacky Wang return stringWriter.toString(); 175*f585d8a3SJacky Wang } 176*f585d8a3SJacky Wang } 177