Home | History | Annotate | Download | only in codegen
      1 /*
      2  * Copyright (C) 2015 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.common.base.Joiner;
     19 import com.google.common.collect.ImmutableList;
     20 import com.google.common.collect.ImmutableSet;
     21 import com.google.testing.compile.JavaFileObjects;
     22 import java.io.IOException;
     23 import java.io.Writer;
     24 import java.util.Set;
     25 import javax.annotation.processing.AbstractProcessor;
     26 import javax.annotation.processing.RoundEnvironment;
     27 import javax.lang.model.element.TypeElement;
     28 import javax.tools.JavaFileObject;
     29 import org.junit.Test;
     30 import org.junit.runner.RunWith;
     31 import org.junit.runners.JUnit4;
     32 
     33 import static com.google.common.truth.Truth.assertAbout;
     34 import static com.google.testing.compile.JavaSourceSubjectFactory.javaSource;
     35 import static com.google.testing.compile.JavaSourcesSubjectFactory.javaSources;
     36 import static javax.tools.StandardLocation.CLASS_OUTPUT;
     37 
     38 @RunWith(JUnit4.class)
     39 public class MembersInjectionTest {
     40   @Test
     41   public void parentClass_noInjectedMembers() {
     42     JavaFileObject childFile = JavaFileObjects.forSourceLines("test.Child",
     43         "package test;",
     44         "",
     45         "import javax.inject.Inject;",
     46         "",
     47         "public final class Child extends Parent {",
     48         "  @Inject Child() {}",
     49         "}");
     50     JavaFileObject parentFile = JavaFileObjects.forSourceLines("test.Parent",
     51         "package test;",
     52         "",
     53         "public abstract class Parent {}");
     54 
     55     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
     56         "package test;",
     57         "",
     58         "import dagger.Component;",
     59         "",
     60         "",
     61         "@Component",
     62         "interface TestComponent {",
     63         "  Child child();",
     64         "}");
     65     JavaFileObject generatedComponent = JavaFileObjects.forSourceLines(
     66         "test.DaggerTestComponent",
     67         "package test;",
     68         "",
     69         "import dagger.MembersInjector;",
     70         "import dagger.internal.MembersInjectors;",
     71         "import javax.annotation.Generated;",
     72         "import javax.inject.Provider;",
     73         "",
     74         "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
     75         "public final class DaggerTestComponent implements TestComponent {",
     76         "  private Provider<Child> childProvider;",
     77         "",
     78         "  private DaggerTestComponent(Builder builder) {",
     79         "    assert builder != null;",
     80         "    initialize(builder);",
     81         "  }",
     82         "",
     83         "  public static Builder builder() {",
     84         "    return new Builder();",
     85         "  }",
     86         "",
     87         "  public static TestComponent create() {",
     88         "    return builder().build();",
     89         "  }",
     90         "",
     91         "  @SuppressWarnings(\"unchecked\")",
     92         "  private void initialize(final Builder builder) {",
     93         "    this.childProvider =",
     94         "        Child_Factory.create((MembersInjector) MembersInjectors.noOp());",
     95         "  }",
     96         "",
     97         "  @Override",
     98         "  public Child child() {",
     99         "    return childProvider.get();",
    100         "  }",
    101         "",
    102         "  public static final class Builder {",
    103         "    private Builder() {",
    104         "    }",
    105         "",
    106         "    public TestComponent build() {",
    107         "      return new DaggerTestComponent(this);",
    108         "    }",
    109         "  }",
    110         "}");
    111     assertAbout(javaSources())
    112         .that(ImmutableList.of(childFile, parentFile, componentFile))
    113         .processedWith(new ComponentProcessor())
    114         .compilesWithoutError()
    115         .and().generatesSources(generatedComponent);
    116   }
    117 
    118   @Test
    119   public void parentClass_injectedMembersInSupertype() {
    120     JavaFileObject childFile = JavaFileObjects.forSourceLines("test.Child",
    121         "package test;",
    122         "",
    123         "import javax.inject.Inject;",
    124         "",
    125         "public final class Child extends Parent {",
    126         "  @Inject Child() {}",
    127         "}");
    128     JavaFileObject parentFile = JavaFileObjects.forSourceLines("test.Parent",
    129         "package test;",
    130         "",
    131         "import javax.inject.Inject;",
    132         "",
    133         "public abstract class Parent {",
    134         "  @Inject Dep dep;",
    135         "}");
    136     JavaFileObject depFile = JavaFileObjects.forSourceLines("test.Dep",
    137         "package test;",
    138         "",
    139         "import javax.inject.Inject;",
    140         "",
    141         "final class Dep {",
    142         "  @Inject Dep() {}",
    143         "}");
    144     JavaFileObject componentFile = JavaFileObjects.forSourceLines("test.TestComponent",
    145         "package test;",
    146         "",
    147         "import dagger.Component;",
    148         "",
    149         "",
    150         "@Component",
    151         "interface TestComponent {",
    152         "  Child child();",
    153         "}");
    154     JavaFileObject generatedComponent =
    155         JavaFileObjects.forSourceLines(
    156             "test.DaggerTestComponent",
    157             "package test;",
    158             "",
    159             "import dagger.MembersInjector;",
    160             "import javax.annotation.Generated;",
    161             "import javax.inject.Provider;",
    162             "",
    163             "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
    164             "public final class DaggerTestComponent implements TestComponent {",
    165             "  private MembersInjector<Child> childMembersInjector;",
    166             "  private Provider<Child> childProvider;",
    167             "",
    168             "  private DaggerTestComponent(Builder builder) {",
    169             "    assert builder != null;",
    170             "    initialize(builder);",
    171             "  }",
    172             "",
    173             "  public static Builder builder() {",
    174             "    return new Builder();",
    175             "  }",
    176             "",
    177             "  public static TestComponent create() {",
    178             "    return builder().build();",
    179             "  }",
    180             "",
    181             "  @SuppressWarnings(\"unchecked\")",
    182             "  private void initialize(final Builder builder) {",
    183             "    this.childMembersInjector = Child_MembersInjector.create(Dep_Factory.create());",
    184             "    this.childProvider = Child_Factory.create(childMembersInjector);",
    185             "  }",
    186             "",
    187             "  @Override",
    188             "  public Child child() {",
    189             "    return childProvider.get();",
    190             "  }",
    191             "",
    192             "  public static final class Builder {",
    193             "    private Builder() {}",
    194             "",
    195             "    public TestComponent build() {",
    196             "      return new DaggerTestComponent(this);",
    197             "    }",
    198             "  }",
    199             "}");
    200     assertAbout(javaSources())
    201         .that(ImmutableList.of(childFile, parentFile, depFile, componentFile))
    202         .processedWith(new ComponentProcessor())
    203         .compilesWithoutError()
    204         .and()
    205         .generatesSources(generatedComponent);
    206   }
    207 
    208   @Test public void fieldAndMethodGenerics() {
    209     JavaFileObject file = JavaFileObjects.forSourceLines("test.GenericClass",
    210         "package test;",
    211         "",
    212         "import javax.inject.Inject;",
    213         "",
    214         "class GenericClass<A, B> {",
    215         "  @Inject A a;",
    216         "",
    217         "  @Inject GenericClass() {}",
    218         "",
    219         " @Inject void register(B b) {}",
    220         "}");
    221     JavaFileObject expected = JavaFileObjects.forSourceLines(
    222         "test.GenericClass_MembersInjector",
    223         "package test;",
    224         "",
    225         "import dagger.MembersInjector;",
    226         "import javax.annotation.Generated;",
    227         "import javax.inject.Provider;",
    228         "",
    229         "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
    230         "public final class GenericClass_MembersInjector<A, B>",
    231         "    implements MembersInjector<GenericClass<A, B>> {",
    232         "  private final Provider<A> aProvider;",
    233         "  private final Provider<B> bProvider;",
    234         "",
    235         "  public GenericClass_MembersInjector(Provider<A> aProvider, Provider<B> bProvider) {",
    236         "    assert aProvider != null;",
    237         "    this.aProvider = aProvider;",
    238         "    assert bProvider != null;",
    239         "    this.bProvider = bProvider;",
    240         "  }",
    241         "",
    242         "  @Override",
    243         "  public void injectMembers(GenericClass<A, B> instance) {",
    244         "    if (instance == null) {",
    245         "      throw new NullPointerException(\"Cannot inject members into a null reference\");",
    246         "    }",
    247         "    instance.a = aProvider.get();",
    248         "    instance.register(bProvider.get());",
    249         "  }",
    250         "",
    251         "  public static <A, B> MembersInjector<GenericClass<A, B>> create(",
    252         "      Provider<A> aProvider, Provider<B> bProvider) {",
    253         "    return new GenericClass_MembersInjector<A, B>(aProvider, bProvider);",
    254         "  }",
    255         "",
    256         "  public static <A, B> void injectA(GenericClass<A, B> instance, Provider<A> aProvider) {",
    257         "    instance.a = aProvider.get();",
    258         "  }",
    259         "",
    260         "  public static <A, B> void injectRegister(",
    261         "      GenericClass<A, B> instance, Provider<B> bProvider) {",
    262         "    instance.register(bProvider.get());",
    263         "  }",
    264         "",
    265         "}");
    266     assertAbout(javaSource())
    267         .that(file)
    268         .processedWith(new ComponentProcessor())
    269         .compilesWithoutError()
    270         .and()
    271         .generatesSources(expected);
    272   }
    273 
    274   @Test public void subclassedGenericMembersInjectors() {
    275     JavaFileObject a = JavaFileObjects.forSourceLines("test.A",
    276         "package test;",
    277         "",
    278         "import javax.inject.Inject;",
    279         "",
    280         "final class A {",
    281         "  @Inject A() {}",
    282         "}");
    283     JavaFileObject a2 = JavaFileObjects.forSourceLines("test.A2",
    284         "package test;",
    285         "",
    286         "import javax.inject.Inject;",
    287         "",
    288         "final class A2 {",
    289         "  @Inject A2() {}",
    290         "}");
    291     JavaFileObject parent = JavaFileObjects.forSourceLines("test.Parent",
    292         "package test;",
    293         "",
    294         "import javax.inject.Inject;",
    295         "",
    296         "class Parent<X, Y> {",
    297         "  @Inject X x;",
    298         "  @Inject Y y;",
    299         "  @Inject A2 a2;",
    300         "",
    301         "  @Inject Parent() {}",
    302         "}");
    303     JavaFileObject child = JavaFileObjects.forSourceLines("test.Child",
    304         "package test;",
    305         "",
    306         "import javax.inject.Inject;",
    307         "",
    308         "class Child<T> extends Parent<T, A> {",
    309         "  @Inject A a;",
    310         "  @Inject T t;",
    311         "",
    312         "  @Inject Child() {}",
    313         "}");
    314     JavaFileObject expected = JavaFileObjects.forSourceLines(
    315         "test.Child_MembersInjector",
    316         "package test;",
    317         "",
    318         "import dagger.MembersInjector;",
    319         "import javax.annotation.Generated;",
    320         "import javax.inject.Provider;",
    321         "",
    322         "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
    323         "public final class Child_MembersInjector<T>",
    324         "    implements MembersInjector<Child<T>> {",
    325         "  private final Provider<T> tAndXProvider;",
    326         "  private final Provider<A> aAndYProvider;",
    327         "  private final Provider<A2> a2Provider;",
    328         "",
    329         "  public Child_MembersInjector(",
    330         "      Provider<T> tAndXProvider, Provider<A> aAndYProvider, Provider<A2> a2Provider) {",
    331         "    assert tAndXProvider != null;",
    332         "    this.tAndXProvider = tAndXProvider;",
    333         "    assert aAndYProvider != null;",
    334         "    this.aAndYProvider = aAndYProvider;",
    335         "    assert a2Provider != null;",
    336         "    this.a2Provider = a2Provider;",
    337         "  }",
    338         "",
    339         "  @Override",
    340         "  public void injectMembers(Child<T> instance) {",
    341         "    if (instance == null) {",
    342         "      throw new NullPointerException(\"Cannot inject members into a null reference\");",
    343         "    }",
    344         "    ((test.Parent) instance).x = tAndXProvider.get();",
    345         "    ((test.Parent) instance).y = aAndYProvider.get();",
    346         "    ((test.Parent) instance).a2 = a2Provider.get();",
    347         "    instance.a = aAndYProvider.get();",
    348         "    instance.t = tAndXProvider.get();",
    349         "  }",
    350         "",
    351         "  public static <T> MembersInjector<Child<T>> create(",
    352         "      Provider<T> tAndXProvider, Provider<A> aAndYProvider, Provider<A2> a2Provider) {",
    353         "    return new Child_MembersInjector<T>(tAndXProvider, aAndYProvider, a2Provider);",
    354         "  }",
    355         "",
    356         "  public static <T> void injectA(Child<T> instance, Provider<A> aProvider) {",
    357         "    instance.a = aProvider.get();",
    358         "  }",
    359         "",
    360         "  public static <T> void injectT(Child<T> instance, Provider<T> tProvider) {",
    361         "    instance.t = tProvider.get();",
    362         "  }",
    363         "}");
    364     assertAbout(javaSources())
    365         .that(ImmutableList.of(a, a2, parent, child))
    366         .processedWith(new ComponentProcessor())
    367         .compilesWithoutError()
    368         .and()
    369         .generatesSources(expected);
    370   }
    371 
    372   @Test public void fieldInjection() {
    373     JavaFileObject file = JavaFileObjects.forSourceLines("test.FieldInjection",
    374         "package test;",
    375         "",
    376         "import dagger.Lazy;",
    377         "import javax.inject.Inject;",
    378         "import javax.inject.Provider;",
    379         "",
    380         "class FieldInjection {",
    381         "  @Inject String string;",
    382         "  @Inject Lazy<String> lazyString;",
    383         "  @Inject Provider<String> stringProvider;",
    384         "}");
    385     JavaFileObject expected = JavaFileObjects.forSourceLines(
    386         "test.FieldInjection_MembersInjector",
    387         "package test;",
    388         "",
    389         "import dagger.MembersInjector;",
    390         "import dagger.internal.DoubleCheckLazy;",
    391         "import javax.annotation.Generated;",
    392         "import javax.inject.Provider;",
    393         "",
    394         "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
    395         "public final class FieldInjection_MembersInjector",
    396         "    implements MembersInjector<FieldInjection> {",
    397         "  private final Provider<String> stringProvider;",
    398         "",
    399         "  public FieldInjection_MembersInjector(Provider<String> stringProvider) {",
    400         "    assert stringProvider != null;",
    401         "    this.stringProvider = stringProvider;",
    402         "  }",
    403         "",
    404         "  @Override",
    405         "  public void injectMembers(FieldInjection instance) {",
    406         "    if (instance == null) {",
    407         "      throw new NullPointerException(\"Cannot inject members into a null reference\");",
    408         "    }",
    409         "    instance.string = stringProvider.get();",
    410         "    instance.lazyString = DoubleCheckLazy.create(stringProvider);",
    411         "    instance.stringProvider = stringProvider;",
    412         "  }",
    413         "",
    414         "  public static MembersInjector<FieldInjection> create(Provider<String> stringProvider) {",
    415         "    return new FieldInjection_MembersInjector(stringProvider);",
    416         "  }",
    417         "",
    418         "  public static void injectString(",
    419         "      FieldInjection instance, Provider<String> stringProvider) {",
    420         "    instance.string = stringProvider.get();",
    421         "  }",
    422         "",
    423         "  public static void injectLazyString(",
    424         "      FieldInjection instance, Provider<String> lazyStringProvider) {",
    425         "    instance.lazyString = DoubleCheckLazy.create(lazyStringProvider);",
    426         "  }",
    427         "",
    428         "  public static void injectStringProvider(",
    429         "      FieldInjection instance, Provider<String> stringProvider) {",
    430         "    instance.stringProvider = stringProvider;",
    431         "  }",
    432         "}");
    433     assertAbout(javaSource())
    434         .that(file)
    435         .processedWith(new ComponentProcessor())
    436         .compilesWithoutError()
    437         .and()
    438         .generatesSources(expected);
    439   }
    440 
    441   @Test public void methodInjection() {
    442     JavaFileObject file = JavaFileObjects.forSourceLines("test.MethodInjection",
    443         "package test;",
    444         "",
    445         "import dagger.Lazy;",
    446         "import javax.inject.Inject;",
    447         "import javax.inject.Provider;",
    448         "",
    449         "class MethodInjection {",
    450         "  @Inject void noArgs() {}",
    451         "  @Inject void oneArg(String string) {}",
    452         "  @Inject void manyArgs(",
    453         "      String string, Lazy<String> lazyString, Provider<String> stringProvider) {}",
    454         "}");
    455     JavaFileObject expected = JavaFileObjects.forSourceLines(
    456         "test.MethodInjection_MembersInjector",
    457         "package test;",
    458         "",
    459         "import dagger.MembersInjector;",
    460         "import dagger.internal.DoubleCheckLazy;",
    461         "import javax.annotation.Generated;",
    462         "import javax.inject.Provider;",
    463         "",
    464         "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
    465         "public final class MethodInjection_MembersInjector",
    466         "     implements MembersInjector<MethodInjection> {",
    467         "",
    468         "  private final Provider<String> stringProvider;",
    469         "",
    470         "  public MethodInjection_MembersInjector(Provider<String> stringProvider) {",
    471         "    assert stringProvider != null;",
    472         "    this.stringProvider = stringProvider;",
    473         "  }",
    474         "",
    475         "  @Override",
    476         "  public void injectMembers(MethodInjection instance) {",
    477         "    if (instance == null) {",
    478         "      throw new NullPointerException(\"Cannot inject members into a null reference\");",
    479         "    }",
    480         "    instance.noArgs();",
    481         "    instance.oneArg(stringProvider.get());",
    482         "    instance.manyArgs(stringProvider.get(), DoubleCheckLazy.create(stringProvider),",
    483         "        stringProvider);",
    484         "  }",
    485         "",
    486         "  public static MembersInjector<MethodInjection> create(",
    487         "      Provider<String> stringProvider) {",
    488         "    return new MethodInjection_MembersInjector(stringProvider);",
    489         "  }",
    490         "",
    491         "  public static void injectNoArgs(MethodInjection instance) {",
    492         "    instance.noArgs();",
    493         "  }",
    494         "",
    495         "  public static void injectOneArg(",
    496         "      MethodInjection instance, Provider<String> stringProvider) {",
    497         "    instance.oneArg(stringProvider.get());",
    498         "  }",
    499         "",
    500         "  public static void injectManyArgs(",
    501         "      MethodInjection instance,",
    502         "      Provider<String> stringProvider,",
    503         "      Provider<String> lazyStringProvider,",
    504         "      Provider<String> stringProvider2) {",
    505         "    instance.manyArgs(",
    506         "        stringProvider.get(),",
    507         "        DoubleCheckLazy.create(lazyStringProvider),",
    508         "        stringProvider2);",
    509         "  }",
    510         "}");
    511     assertAbout(javaSource())
    512         .that(file)
    513         .processedWith(new ComponentProcessor())
    514         .compilesWithoutError()
    515         .and()
    516         .generatesSources(expected);
    517   }
    518 
    519   @Test
    520   public void mixedMemberInjection() {
    521     JavaFileObject file = JavaFileObjects.forSourceLines(
    522         "test.MixedMemberInjection",
    523         "package test;",
    524         "",
    525         "import dagger.Lazy;",
    526         "import javax.inject.Inject;",
    527         "import javax.inject.Provider;",
    528         "",
    529         "class MixedMemberInjection {",
    530         "  @Inject String string;",
    531         "  @Inject void setString(String s) {}",
    532         "  @Inject Object object;",
    533         "  @Inject void setObject(Object o) {}",
    534         "}");
    535     JavaFileObject expected = JavaFileObjects.forSourceLines(
    536         "test.MixedMemberInjection_MembersInjector",
    537         "package test;",
    538         "",
    539         "import dagger.MembersInjector;",
    540         "import javax.annotation.Generated;",
    541         "import javax.inject.Provider;",
    542         "",
    543         "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
    544         "public final class MixedMemberInjection_MembersInjector",
    545         "    implements MembersInjector<MixedMemberInjection> {",
    546         "",
    547         "  private final Provider<String> stringAndSProvider;",
    548         "  private final Provider<Object> objectAndOProvider;",
    549         "",
    550         "  public MixedMemberInjection_MembersInjector(",
    551         "      Provider<String> stringAndSProvider,",
    552         "      Provider<Object> objectAndOProvider) {",
    553         "    assert stringAndSProvider != null;",
    554         "    this.stringAndSProvider = stringAndSProvider;",
    555         "    assert objectAndOProvider != null;",
    556         "    this.objectAndOProvider = objectAndOProvider;",
    557         "  }",
    558         "",
    559         "  @Override",
    560         "  public void injectMembers(MixedMemberInjection instance) {",
    561         "    if (instance == null) {",
    562         "      throw new NullPointerException(\"Cannot inject members into a null reference\");",
    563         "    }",
    564         "    instance.string = stringAndSProvider.get();",
    565         "    instance.object = objectAndOProvider.get();",
    566         "    instance.setString(stringAndSProvider.get());",
    567         "    instance.setObject(objectAndOProvider.get());",
    568         "  }",
    569         "",
    570         "  public static MembersInjector<MixedMemberInjection> create(",
    571         "      Provider<String> stringAndSProvider,",
    572         "      Provider<Object> objectAndOProvider) {",
    573         "    return new MixedMemberInjection_MembersInjector(",
    574         "        stringAndSProvider, objectAndOProvider);",
    575         "  }",
    576         "  public static void injectString(",
    577         "      MixedMemberInjection instance, Provider<String> stringProvider) {",
    578         "    instance.string = stringProvider.get();",
    579         "  }",
    580         "",
    581         "  public static void injectObject(",
    582         "      MixedMemberInjection instance, Provider<Object> objectProvider) {",
    583         "    instance.object = objectProvider.get();",
    584         "  }",
    585         "",
    586         "  public static void injectSetString(",
    587         "      MixedMemberInjection instance, Provider<String> sProvider) {",
    588         "    instance.setString(sProvider.get());",
    589         "  }",
    590         "",
    591         "  public static void injectSetObject(",
    592         "      MixedMemberInjection instance, Provider<Object> oProvider) {",
    593         "    instance.setObject(oProvider.get());",
    594         "  }",
    595         "}");
    596     assertAbout(javaSource())
    597         .that(file)
    598         .processedWith(new ComponentProcessor())
    599         .compilesWithoutError()
    600         .and()
    601         .generatesSources(expected);
    602   }
    603 
    604   @Test public void injectConstructorAndMembersInjection() {
    605     JavaFileObject file = JavaFileObjects.forSourceLines("test.AllInjections",
    606         "package test;",
    607         "",
    608         "import javax.inject.Inject;",
    609         "",
    610         "class AllInjections {",
    611         "  @Inject String s;",
    612         "  @Inject AllInjections(String s) {}",
    613         "  @Inject void s(String s) {}",
    614         "}");
    615     JavaFileObject expectedMembersInjector = JavaFileObjects.forSourceLines(
    616         "test.AllInjections_MembersInjector",
    617         "package test;",
    618         "",
    619         "import dagger.MembersInjector;",
    620         "import javax.annotation.Generated;",
    621         "import javax.inject.Provider;",
    622         "",
    623         "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
    624         "public final class AllInjections_MembersInjector ",
    625         "    implements MembersInjector<AllInjections> {",
    626         "",
    627         "  private final Provider<String> sProvider;",
    628         "",
    629         "  public AllInjections_MembersInjector(Provider<String> sProvider) {",
    630         "    assert sProvider != null;",
    631         "    this.sProvider = sProvider;",
    632         "  }",
    633         "",
    634         "  @Override",
    635         "  public void injectMembers(AllInjections instance) {",
    636         "    if (instance == null) {",
    637         "      throw new NullPointerException(\"Cannot inject members into a null reference\");",
    638         "    }",
    639         "    instance.s = sProvider.get();",
    640         "    instance.s(sProvider.get());",
    641         "  }",
    642         "",
    643         "  public static MembersInjector<AllInjections> create(Provider<String> sProvider) {",
    644         "      return new AllInjections_MembersInjector(sProvider);",
    645         "  }",
    646         "",
    647         "  public static void injectS(AllInjections instance, Provider<String> sProvider) {",
    648         "    instance.s = sProvider.get();",
    649         "  }",
    650         "}");
    651     assertAbout(javaSource())
    652         .that(file)
    653         .processedWith(new ComponentProcessor())
    654         .compilesWithoutError()
    655         .and()
    656         .generatesSources(expectedMembersInjector);
    657   }
    658 
    659   @Test public void supertypeMembersInjection() {
    660     JavaFileObject aFile = JavaFileObjects.forSourceLines("test.A",
    661         "package test;",
    662         "",
    663         "class A {}");
    664     JavaFileObject bFile = JavaFileObjects.forSourceLines("test.B",
    665         "package test;",
    666         "",
    667         "import javax.inject.Inject;",
    668         "",
    669         "class B extends A {",
    670         "  @Inject String s;",
    671         "}");
    672     JavaFileObject expectedMembersInjector = JavaFileObjects.forSourceLines(
    673         "test.AllInjections_MembersInjector",
    674         "package test;",
    675         "",
    676         "import dagger.MembersInjector;",
    677         "import javax.annotation.Generated;",
    678         "import javax.inject.Provider;",
    679         "",
    680         "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
    681         "public final class B_MembersInjector implements MembersInjector<B> {",
    682         "  private final Provider<String> sProvider;",
    683         "",
    684         "  public B_MembersInjector(Provider<String> sProvider) {",
    685         "    assert sProvider != null;",
    686         "    this.sProvider = sProvider;",
    687         "  }",
    688         "",
    689         "  @Override",
    690         "  public void injectMembers(B instance) {",
    691         "    if (instance == null) {",
    692         "      throw new NullPointerException(\"Cannot inject members into a null reference\");",
    693         "    }",
    694         "    instance.s = sProvider.get();",
    695         "  }",
    696         "",
    697         "  public static MembersInjector<B> create(Provider<String> sProvider) {",
    698         "      return new B_MembersInjector(sProvider);",
    699         "  }",
    700         "  public static void injectS(B instance, Provider<String> sProvider) {",
    701         "    instance.s = sProvider.get();",
    702         "  }",
    703         "}");
    704     assertAbout(javaSources())
    705         .that(ImmutableList.of(aFile, bFile))
    706         .processedWith(new ComponentProcessor())
    707         .compilesWithoutError()
    708         .and()
    709         .generatesSources(expectedMembersInjector);
    710   }
    711 
    712   @Test
    713   public void simpleComponentWithNesting() {
    714     JavaFileObject nestedTypesFile = JavaFileObjects.forSourceLines(
    715           "test.OuterType",
    716           "package test;",
    717           "",
    718           "import dagger.Component;",
    719           "import javax.inject.Inject;",
    720           "",
    721           "final class OuterType {",
    722           "  static class A {",
    723           "    @Inject A() {}",
    724           "  }",
    725           "  static class B {",
    726           "    @Inject A a;",
    727           "  }",
    728           "  @Component interface SimpleComponent {",
    729           "    A a();",
    730           "    void inject(B b);",
    731           "  }",
    732           "}");
    733     JavaFileObject bMembersInjector = JavaFileObjects.forSourceLines(
    734           "test.OuterType$B_MembersInjector",
    735           "package test;",
    736           "",
    737           "import dagger.MembersInjector;",
    738           "import javax.annotation.Generated;",
    739           "import javax.inject.Provider;",
    740           "import test.OuterType.A;",
    741           "import test.OuterType.B;",
    742           "",
    743           "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
    744           "public final class OuterType$B_MembersInjector implements MembersInjector<B> {",
    745           "  private final Provider<A> aProvider;",
    746           "",
    747           "  public OuterType$B_MembersInjector(Provider<A> aProvider) {",
    748           "    assert aProvider != null;",
    749           "    this.aProvider = aProvider;",
    750           "  }",
    751           "",
    752           "  @Override",
    753           "  public void injectMembers(B instance) {",
    754           "    if (instance == null) {",
    755           "      throw new NullPointerException(\"Cannot inject members into a null reference\");",
    756           "    }",
    757           "    instance.a = aProvider.get();",
    758           "  }",
    759           "",
    760           "  public static MembersInjector<B> create(Provider<A> aProvider) {",
    761           "    return new OuterType$B_MembersInjector(aProvider);",
    762           "  }",
    763           "",
    764           "  public static void injectA(B instance, Provider<A> aProvider) {",
    765           "    instance.a = aProvider.get();",
    766           "  }",
    767           "}");
    768     assertAbout(javaSources())
    769         .that(ImmutableList.of(nestedTypesFile))
    770         .processedWith(new ComponentProcessor())
    771         .compilesWithoutError()
    772         .and()
    773         .generatesSources(bMembersInjector);
    774   }
    775 
    776   @Test
    777   public void componentWithNestingAndGeneratedType() {
    778     JavaFileObject nestedTypesFile =
    779         JavaFileObjects.forSourceLines(
    780             "test.OuterType",
    781             "package test;",
    782             "",
    783             "import dagger.Component;",
    784             "import javax.inject.Inject;",
    785             "",
    786             "final class OuterType {",
    787             "  @Inject GeneratedType generated;",
    788             "  static class A {",
    789             "    @Inject A() {}",
    790             "  }",
    791             "  static class B {",
    792             "    @Inject A a;",
    793             "  }",
    794             "  @Component interface SimpleComponent {",
    795             "    A a();",
    796             "    void inject(B b);",
    797             "  }",
    798             "}");
    799     JavaFileObject bMembersInjector =
    800         JavaFileObjects.forSourceLines(
    801             "test.OuterType$B_MembersInjector",
    802             "package test;",
    803             "",
    804             "import dagger.MembersInjector;",
    805             "import javax.annotation.Generated;",
    806             "import javax.inject.Provider;",
    807             "import test.OuterType.A;",
    808             "import test.OuterType.B;",
    809             "",
    810             "@Generated(\"dagger.internal.codegen.ComponentProcessor\")",
    811             "public final class OuterType$B_MembersInjector implements MembersInjector<B> {",
    812             "  private final Provider<A> aProvider;",
    813             "",
    814             "  public OuterType$B_MembersInjector(Provider<A> aProvider) {",
    815             "    assert aProvider != null;",
    816             "    this.aProvider = aProvider;",
    817             "  }",
    818             "",
    819             "  @Override",
    820             "  public void injectMembers(B instance) {",
    821             "    if (instance == null) {",
    822             "      throw new NullPointerException(\"Cannot inject members into a null reference\");",
    823             "    }",
    824             "    instance.a = aProvider.get();",
    825             "  }",
    826             "",
    827             "  public static MembersInjector<B> create(Provider<A> aProvider) {",
    828             "    return new OuterType$B_MembersInjector(aProvider);",
    829             "  }",
    830             "",
    831             "  public static void injectA(B instance, Provider<A> aProvider) {",
    832             "    instance.a = aProvider.get();",
    833             "  }",
    834             "}");
    835     assertAbout(javaSource())
    836         .that(nestedTypesFile)
    837         .processedWith(
    838             new ComponentProcessor(),
    839             new AbstractProcessor() {
    840               private boolean done;
    841 
    842               @Override
    843               public Set<String> getSupportedAnnotationTypes() {
    844                 return ImmutableSet.of("*");
    845               }
    846 
    847               @Override
    848               public boolean process(
    849                   Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    850                 if (!done) {
    851                   done = true;
    852                   try (Writer writer =
    853                           processingEnv
    854                               .getFiler()
    855                               .createSourceFile("test.GeneratedType")
    856                               .openWriter()) {
    857                     writer.write(
    858                         Joiner.on('\n')
    859                             .join(
    860                                 "package test;",
    861                                 "",
    862                                 "import javax.inject.Inject;",
    863                                 "",
    864                                 "class GeneratedType {",
    865                                 "  @Inject GeneratedType() {}",
    866                                 "}"));
    867                   } catch (IOException e) {
    868                     throw new RuntimeException(e);
    869                   }
    870                 }
    871                 return false;
    872               }
    873             })
    874         .compilesWithoutError()
    875         .and()
    876         .generatesSources(bMembersInjector);
    877   }
    878 
    879   @Test
    880   public void lowerCaseNamedMembersInjector_forLowerCaseType() {
    881     JavaFileObject foo =
    882         JavaFileObjects.forSourceLines(
    883             "test.foo",
    884             "package test;",
    885             "",
    886             "import javax.inject.Inject;",
    887             "",
    888             "class foo {",
    889             "  @Inject String string;",
    890             "}");
    891     JavaFileObject fooModule =
    892         JavaFileObjects.forSourceLines(
    893             "test.fooModule",
    894             "package test;",
    895             "",
    896             "import dagger.Module;",
    897             "import dagger.Provides;",
    898             "",
    899             "@Module",
    900             "class fooModule {",
    901             "  @Provides String string() { return \"foo\"; }",
    902             "}");
    903     JavaFileObject fooComponent =
    904         JavaFileObjects.forSourceLines(
    905             "test.fooComponent",
    906             "package test;",
    907             "",
    908             "import dagger.Component;",
    909             "",
    910             "@Component(modules = fooModule.class)",
    911             "interface fooComponent {",
    912             "  void inject(foo target);",
    913             "}");
    914 
    915     assertAbout(javaSources())
    916         .that(ImmutableList.of(foo, fooModule, fooComponent))
    917         .processedWith(new ComponentProcessor())
    918         .compilesWithoutError()
    919         .and().generatesFileNamed(CLASS_OUTPUT, "test", "foo_MembersInjector.class");
    920   }
    921 }
    922