Home | History | Annotate | Download | only in codegen
      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