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