1 /* 2 * Copyright (C) 2014 Google, Inc. 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 package dagger.internal.codegen; 17 18 import com.google.testing.compile.JavaFileObjects; 19 import java.util.Arrays; 20 import javax.tools.JavaFileObject; 21 import org.junit.Test; 22 import org.junit.runner.RunWith; 23 import org.junit.runners.JUnit4; 24 25 import static com.google.common.truth.Truth.assertAbout; 26 import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource; 27 import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources; 28 29 /** 30 * Unit tests for {@link BindingGraphValidator} that exercise producer-specific logic. 31 */ 32 @RunWith(JUnit4.class) 33 public class ProductionGraphValidationTest { 34 @Test public void componentWithUnprovidedInput() { 35 JavaFileObject component = JavaFileObjects.forSourceLines("test.MyComponent", 36 "package test;", 37 "", 38 "import com.google.common.util.concurrent.ListenableFuture;", 39 "import dagger.producers.ProductionComponent;", 40 "", 41 "@ProductionComponent(modules = FooModule.class)", 42 "interface MyComponent {", 43 " ListenableFuture<Foo> getFoo();", 44 "}"); 45 JavaFileObject module = JavaFileObjects.forSourceLines("test.FooModule", 46 "package test;", 47 "", 48 "import dagger.producers.ProducerModule;", 49 "import dagger.producers.Produces;", 50 "", 51 "class Foo {}", 52 "class Bar {}", 53 "", 54 "@ProducerModule", 55 "class FooModule {", 56 " @Produces Foo foo(Bar bar) {", 57 " return null;", 58 " }", 59 "}"); 60 assertAbout(javaSources()).that(Arrays.asList(module, component)) 61 .processedWith(new ComponentProcessor()) 62 .failsToCompile() 63 .withErrorContaining("test.Bar cannot be provided without an @Inject constructor or from " 64 + "an @Provides- or @Produces-annotated method.") 65 .in(component).onLine(8); 66 } 67 68 @Test public void componentProductionWithNoDependencyChain() { 69 JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass", 70 "package test;", 71 "", 72 "import com.google.common.util.concurrent.ListenableFuture;", 73 "import dagger.producers.ProductionComponent;", 74 "", 75 "final class TestClass {", 76 " interface A {}", 77 "", 78 " @ProductionComponent()", 79 " interface AComponent {", 80 " ListenableFuture<A> getA();", 81 " }", 82 "}"); 83 String expectedError = 84 "test.TestClass.A cannot be provided without an @Provides- or @Produces-annotated method."; 85 assertAbout(javaSource()).that(component) 86 .processedWith(new ComponentProcessor()) 87 .failsToCompile() 88 .withErrorContaining(expectedError).in(component).onLine(11); 89 } 90 91 @Test public void provisionDependsOnProduction() { 92 JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass", 93 "package test;", 94 "", 95 "import com.google.common.util.concurrent.ListenableFuture;", 96 "import dagger.Module;", 97 "import dagger.Provides;", 98 "import dagger.producers.ProducerModule;", 99 "import dagger.producers.Produces;", 100 "import dagger.producers.ProductionComponent;", 101 "", 102 "final class TestClass {", 103 " interface A {}", 104 " interface B {}", 105 "", 106 " @Module", 107 " final class AModule {", 108 " @Provides A a(B b) {", 109 " return null;", 110 " }", 111 " }", 112 "", 113 " @ProducerModule", 114 " final class BModule {", 115 " @Produces ListenableFuture<B> b() {", 116 " return null;", 117 " }", 118 " }", 119 "", 120 " @ProductionComponent(modules = {AModule.class, BModule.class})", 121 " interface AComponent {", 122 " ListenableFuture<A> getA();", 123 " }", 124 "}"); 125 String expectedError = 126 "test.TestClass.A is a provision, which cannot depend on a production."; 127 assertAbout(javaSource()).that(component) 128 .processedWith(new ComponentProcessor()) 129 .failsToCompile() 130 .withErrorContaining(expectedError).in(component).onLine(30); 131 } 132 133 @Test public void provisionEntryPointDependsOnProduction() { 134 JavaFileObject component = JavaFileObjects.forSourceLines("test.TestClass", 135 "package test;", 136 "", 137 "import com.google.common.util.concurrent.ListenableFuture;", 138 "import dagger.producers.ProducerModule;", 139 "import dagger.producers.Produces;", 140 "import dagger.producers.ProductionComponent;", 141 "", 142 "final class TestClass {", 143 " interface A {}", 144 "", 145 " @ProducerModule", 146 " final class AModule {", 147 " @Produces ListenableFuture<A> a() {", 148 " return null;", 149 " }", 150 " }", 151 "", 152 " @ProductionComponent(modules = AModule.class)", 153 " interface AComponent {", 154 " A getA();", 155 " }", 156 "}"); 157 String expectedError = 158 "test.TestClass.A is a provision entry-point, which cannot depend on a production."; 159 assertAbout(javaSource()).that(component) 160 .processedWith(new ComponentProcessor()) 161 .failsToCompile() 162 .withErrorContaining(expectedError).in(component).onLine(20); 163 } 164 165 @Test 166 public void monitoringDependsOnUnboundType() { 167 JavaFileObject component = 168 JavaFileObjects.forSourceLines( 169 "test.TestClass", 170 "package test;", 171 "", 172 "import com.google.common.util.concurrent.ListenableFuture;", 173 "import dagger.Module;", 174 "import dagger.Provides;", 175 "import dagger.producers.ProducerModule;", 176 "import dagger.producers.Produces;", 177 "import dagger.producers.ProductionComponent;", 178 "import dagger.producers.monitoring.ProductionComponentMonitor;", 179 "", 180 "import static dagger.Provides.Type.SET;", 181 "", 182 "final class TestClass {", 183 " interface A {}", 184 "", 185 " @Module", 186 " final class MonitoringModule {", 187 " @Provides(type = SET)", 188 " ProductionComponentMonitor.Factory monitorFactory(A unbound) {", 189 " return null;", 190 " }", 191 " }", 192 "", 193 " @ProducerModule", 194 " final class StringModule {", 195 " @Produces ListenableFuture<String> str() {", 196 " return null;", 197 " }", 198 " }", 199 "", 200 " @ProductionComponent(modules = {MonitoringModule.class, StringModule.class})", 201 " interface StringComponent {", 202 " ListenableFuture<String> getString();", 203 " }", 204 "}"); 205 String expectedError = 206 "test.TestClass.A cannot be provided without an @Provides-annotated method."; 207 assertAbout(javaSource()) 208 .that(component) 209 .processedWith(new ComponentProcessor()) 210 .failsToCompile() 211 .withErrorContaining(expectedError) 212 .in(component) 213 .onLine(33); 214 } 215 216 @Test 217 public void monitoringDependsOnProduction() { 218 JavaFileObject component = 219 JavaFileObjects.forSourceLines( 220 "test.TestClass", 221 "package test;", 222 "", 223 "import com.google.common.util.concurrent.ListenableFuture;", 224 "import dagger.Module;", 225 "import dagger.Provides;", 226 "import dagger.producers.ProducerModule;", 227 "import dagger.producers.Produces;", 228 "import dagger.producers.ProductionComponent;", 229 "import dagger.producers.monitoring.ProductionComponentMonitor;", 230 "", 231 "import static dagger.Provides.Type.SET;", 232 "", 233 "final class TestClass {", 234 " interface A {}", 235 "", 236 " @Module", 237 " final class MonitoringModule {", 238 " @Provides(type = SET) ProductionComponentMonitor.Factory monitorFactory(A a) {", 239 " return null;", 240 " }", 241 " }", 242 "", 243 " @ProducerModule", 244 " final class StringModule {", 245 " @Produces A a() {", 246 " return null;", 247 " }", 248 "", 249 " @Produces ListenableFuture<String> str() {", 250 " return null;", 251 " }", 252 " }", 253 "", 254 " @ProductionComponent(modules = {MonitoringModule.class, StringModule.class})", 255 " interface StringComponent {", 256 " ListenableFuture<String> getString();", 257 " }", 258 "}"); 259 String expectedError = 260 "java.util.Set<dagger.producers.monitoring.ProductionComponentMonitor.Factory> is a" 261 + " provision, which cannot depend on a production."; 262 assertAbout(javaSource()) 263 .that(component) 264 .processedWith(new ComponentProcessor()) 265 .failsToCompile() 266 .withErrorContaining(expectedError) 267 .in(component) 268 .onLine(36); 269 } 270 } 271