• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.testing.compile.CompilationSubject.assertThat;
20 import static dagger.internal.codegen.Compilers.daggerCompiler;
21 import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatMethodInUnannotatedClass;
22 import static dagger.internal.codegen.DaggerModuleMethodSubject.Factory.assertThatModuleMethod;
23 
24 import com.google.common.collect.ImmutableList;
25 import com.google.testing.compile.Compilation;
26 import com.google.testing.compile.JavaFileObjects;
27 import dagger.Module;
28 import dagger.producers.ProducerModule;
29 import java.lang.annotation.Annotation;
30 import java.util.Collection;
31 import javax.inject.Inject;
32 import javax.inject.Qualifier;
33 import javax.inject.Singleton;
34 import javax.tools.JavaFileObject;
35 import org.junit.Test;
36 import org.junit.runner.RunWith;
37 import org.junit.runners.Parameterized;
38 import org.junit.runners.Parameterized.Parameters;
39 
40 /** Tests {@link dagger.internal.codegen.validation.BindsOptionalOfMethodValidator}. */
41 @RunWith(Parameterized.class)
42 public class BindsOptionalOfMethodValidationTest {
43   @Parameters(name = "{0}")
data()44   public static Collection<Object[]> data() {
45     return ImmutableList.copyOf(new Object[][] {{Module.class}, {ProducerModule.class}});
46   }
47 
48   private final String moduleDeclaration;
49 
BindsOptionalOfMethodValidationTest(Class<? extends Annotation> moduleAnnotation)50   public BindsOptionalOfMethodValidationTest(Class<? extends Annotation> moduleAnnotation) {
51     moduleDeclaration = "@" + moduleAnnotation.getCanonicalName() + " abstract class %s { %s }";
52   }
53 
54   @Test
nonAbstract()55   public void nonAbstract() {
56     assertThatMethod("@BindsOptionalOf Object concrete() { return null; }")
57         .hasError("must be abstract");
58   }
59 
60   @Test
hasParameters()61   public void hasParameters() {
62     assertThatMethod("@BindsOptionalOf abstract Object hasParameters(String s1);")
63         .hasError("parameters");
64   }
65 
66   @Test
typeParameters()67   public void typeParameters() {
68     assertThatMethod("@BindsOptionalOf abstract <S> S generic();").hasError("type parameters");
69   }
70 
71   @Test
notInModule()72   public void notInModule() {
73     assertThatMethodInUnannotatedClass("@BindsOptionalOf abstract Object notInModule();")
74         .hasError("within a @Module or @ProducerModule");
75   }
76 
77   @Test
throwsException()78   public void throwsException() {
79     assertThatMethod("@BindsOptionalOf abstract Object throwsException() throws RuntimeException;")
80         .hasError("may not throw");
81   }
82 
83   @Test
returnsVoid()84   public void returnsVoid() {
85     assertThatMethod("@BindsOptionalOf abstract void returnsVoid();").hasError("void");
86   }
87 
88   @Test
returnsMembersInjector()89   public void returnsMembersInjector() {
90     assertThatMethod("@BindsOptionalOf abstract MembersInjector<Object> returnsMembersInjector();")
91         .hasError("framework");
92   }
93 
94   @Test
tooManyQualifiers()95   public void tooManyQualifiers() {
96     assertThatMethod(
97             "@BindsOptionalOf @Qualifier1 @Qualifier2 abstract String tooManyQualifiers();")
98         .importing(Qualifier1.class, Qualifier2.class)
99         .hasError("more than one @Qualifier");
100   }
101 
102   @Test
intoSet()103   public void intoSet() {
104     assertThatMethod("@BindsOptionalOf @IntoSet abstract String intoSet();")
105         .hasError("cannot have multibinding annotations");
106   }
107 
108   @Test
elementsIntoSet()109   public void elementsIntoSet() {
110     assertThatMethod("@BindsOptionalOf @ElementsIntoSet abstract Set<String> elementsIntoSet();")
111         .hasError("cannot have multibinding annotations");
112   }
113 
114   @Test
intoMap()115   public void intoMap() {
116     assertThatMethod("@BindsOptionalOf @IntoMap abstract String intoMap();")
117         .hasError("cannot have multibinding annotations");
118   }
119 
120   /**
121    * Tests that @BindsOptionalOf @IntoMap actually causes module validation to fail.
122    *
123    * @see <a href="http://b/118434447">bug 118434447</a>
124    */
125   @Test
intoMapWithComponent()126   public void intoMapWithComponent() {
127     JavaFileObject module =
128         JavaFileObjects.forSourceLines(
129             "test.TestModule",
130             "package test;",
131             "",
132             "import dagger.BindsOptionalOf;",
133             "import dagger.Module;",
134             "import dagger.multibindings.IntoMap;",
135             "",
136             "@Module",
137             "interface TestModule {",
138             "  @BindsOptionalOf @IntoMap Object object();",
139             "}");
140     JavaFileObject component =
141         JavaFileObjects.forSourceLines(
142             "test.TestComponent",
143             "package test;",
144             "",
145             "import dagger.Component;",
146             "",
147             "@Component(modules = TestModule.class)",
148             "interface TestComponent {}");
149 
150     Compilation compilation = daggerCompiler().compile(module, component);
151     assertThat(compilation).failed();
152     assertThat(compilation)
153         .hadErrorContaining("cannot have multibinding annotations")
154         .inFile(module)
155         .onLineContaining("object();");
156   }
157 
158   /** An injectable value object. */
159   public static final class Thing {
160     @Inject
Thing()161     Thing() {}
162   }
163 
164   @Test
implicitlyProvidedType()165   public void implicitlyProvidedType() {
166     assertThatMethod("@BindsOptionalOf abstract Thing thing();")
167         .importing(Thing.class)
168         .hasError("return unqualified types that have an @Inject-annotated constructor");
169   }
170 
171   @Test
hasScope()172   public void hasScope() {
173     assertThatMethod("@BindsOptionalOf @Singleton abstract String scoped();")
174         .importing(Singleton.class)
175         .hasError("cannot be scoped");
176   }
177 
assertThatMethod(String method)178   private DaggerModuleMethodSubject assertThatMethod(String method) {
179     return assertThatModuleMethod(method).withDeclaration(moduleDeclaration);
180   }
181 
182   /** A qualifier. */
183   @Qualifier
184   public @interface Qualifier1 {}
185 
186   /** A qualifier. */
187   @Qualifier
188   public @interface Qualifier2 {}
189 }
190