1 /*
2  * Copyright (C) 2017 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.CompilerMode.DEFAULT_MODE;
21 import static dagger.internal.codegen.CompilerMode.FAST_INIT_MODE;
22 import static dagger.internal.codegen.Compilers.compilerWithOptions;
23 import static dagger.internal.codegen.GeneratedLines.GENERATED_CODE_ANNOTATIONS;
24 
25 import com.google.testing.compile.Compilation;
26 import com.google.testing.compile.JavaFileObjects;
27 import java.util.Collection;
28 import javax.tools.JavaFileObject;
29 import org.junit.Test;
30 import org.junit.runner.RunWith;
31 import org.junit.runners.Parameterized;
32 import org.junit.runners.Parameterized.Parameters;
33 
34 @RunWith(Parameterized.class)
35 public class OptionalBindingRequestFulfillmentTest {
36   @Parameters(name = "{0}")
parameters()37   public static Collection<Object[]> parameters() {
38     return CompilerMode.TEST_PARAMETERS;
39   }
40 
41   private final CompilerMode compilerMode;
42 
OptionalBindingRequestFulfillmentTest(CompilerMode compilerMode)43   public OptionalBindingRequestFulfillmentTest(CompilerMode compilerMode) {
44     this.compilerMode = compilerMode;
45   }
46 
47   @Test
inlinedOptionalBindings()48   public void inlinedOptionalBindings() {
49     JavaFileObject module =
50         JavaFileObjects.forSourceLines(
51             "test.TestModule",
52             "package test;",
53             "",
54             "import dagger.Module;",
55             "import dagger.BindsOptionalOf;",
56             "import other.Maybe;",
57             "import other.DefinitelyNot;",
58             "",
59             "@Module",
60             "interface TestModule {",
61             "  @BindsOptionalOf Maybe maybe();",
62             "  @BindsOptionalOf DefinitelyNot definitelyNot();",
63             "}");
64     JavaFileObject maybe =
65         JavaFileObjects.forSourceLines(
66             "other.Maybe",
67             "package other;",
68             "",
69             "import dagger.Module;",
70             "import dagger.Provides;",
71             "",
72             "public class Maybe {",
73             "  @Module",
74             "  public static class MaybeModule {",
75             "    @Provides static Maybe provideMaybe() { return new Maybe(); }",
76             "  }",
77             "}");
78     JavaFileObject definitelyNot =
79         JavaFileObjects.forSourceLines(
80             "other.DefinitelyNot",
81             "package other;",
82             "",
83             "public class DefinitelyNot {}");
84 
85     JavaFileObject component =
86         JavaFileObjects.forSourceLines(
87             "test.TestComponent",
88             "package test;",
89             "",
90             "import com.google.common.base.Optional;",
91             "import dagger.Component;",
92             "import dagger.Lazy;",
93             "import javax.inject.Provider;",
94             "import other.Maybe;",
95             "import other.DefinitelyNot;",
96             "",
97             "@Component(modules = {TestModule.class, Maybe.MaybeModule.class})",
98             "interface TestComponent {",
99             "  Optional<Maybe> maybe();",
100             "  Optional<Provider<Lazy<Maybe>>> providerOfLazyOfMaybe();",
101             "  Optional<DefinitelyNot> definitelyNot();",
102             "  Optional<Provider<Lazy<DefinitelyNot>>> providerOfLazyOfDefinitelyNot();",
103             "}");
104 
105     JavaFileObject generatedComponent =
106         compilerMode
107             .javaFileBuilder("test.DaggerTestComponent")
108             .addLines(
109                 "package test;",
110                 "",
111                 "import com.google.common.base.Optional;",
112                 "",
113                 GENERATED_CODE_ANNOTATIONS,
114                 "final class DaggerTestComponent implements TestComponent {")
115             .addLinesIn(
116                 FAST_INIT_MODE,
117                 "  private volatile Provider<Maybe> provideMaybeProvider;",
118                 "",
119                 "  private Provider<Maybe> maybeProvider() {",
120                 "    Object local = provideMaybeProvider;",
121                 "    if (local == null) {",
122                 "      local = new SwitchingProvider<>(0);",
123                 "      provideMaybeProvider = (Provider<Maybe>) local;",
124                 "    }",
125                 "    return (Provider<Maybe>) local;",
126                 "  }")
127             .addLines(
128                 "  @Override",
129                 "  public Optional<Maybe> maybe() {",
130                 "    return Optional.of(",
131                 "        Maybe_MaybeModule_ProvideMaybeFactory.provideMaybe());",
132                 "  }",
133                 "",
134                 "  @Override",
135                 "  public Optional<Provider<Lazy<Maybe>>> providerOfLazyOfMaybe() {",
136                 "    return Optional.of(ProviderOfLazy.create(")
137             .addLinesIn(
138                 DEFAULT_MODE, //
139                 "        Maybe_MaybeModule_ProvideMaybeFactory.create()));")
140             .addLinesIn(
141                 FAST_INIT_MODE, //
142                 "        maybeProvider()));")
143             .addLines(
144                 "  }",
145                 "",
146                 "  @Override",
147                 "  public Optional<DefinitelyNot> definitelyNot() {",
148                 "    return Optional.<DefinitelyNot>absent();",
149                 "  }",
150                 "",
151                 "  @Override",
152                 "  public Optional<Provider<Lazy<DefinitelyNot>>>",
153                 "      providerOfLazyOfDefinitelyNot() {",
154                 "    return Optional.<Provider<Lazy<DefinitelyNot>>>absent();",
155                 "  }")
156             .addLinesIn(
157                 FAST_INIT_MODE,
158                 "  private final class SwitchingProvider<T> implements Provider<T> {",
159                 "    private final int id;",
160                 "",
161                 "    SwitchingProvider(int id) {",
162                 "      this.id = id;",
163                 "    }",
164                 "",
165                 "    @SuppressWarnings(\"unchecked\")",
166                 "    @Override",
167                 "    public T get() {",
168                 "      switch (id) {",
169                 "        case 0:",
170                 "          return (T) Maybe_MaybeModule_ProvideMaybeFactory.provideMaybe();",
171                 "        default:",
172                 "          throw new AssertionError(id);",
173                 "      }",
174                 "    }",
175                 "  }",
176                 "}")
177             .build();
178     Compilation compilation =
179         compilerWithOptions(
180                 compilerMode
181                 , CompilerMode.JAVA7
182                 )
183             .compile(module, maybe, definitelyNot, component);
184     assertThat(compilation).succeeded();
185     assertThat(compilation)
186         .generatedSourceFile("test.DaggerTestComponent")
187         .containsElementsIn(generatedComponent);
188   }
189 
190   @Test
requestForFuture()191   public void requestForFuture() {
192     JavaFileObject module =
193         JavaFileObjects.forSourceLines(
194             "test.TestModule",
195             "package test;",
196             "",
197             "import dagger.Module;",
198             "import dagger.BindsOptionalOf;",
199             "import other.Maybe;",
200             "import other.DefinitelyNot;",
201             "",
202             "@Module",
203             "interface TestModule {",
204             "  @BindsOptionalOf Maybe maybe();",
205             "  @BindsOptionalOf DefinitelyNot definitelyNot();",
206             "}");
207     JavaFileObject maybe =
208         JavaFileObjects.forSourceLines(
209             "other.Maybe",
210             "package other;",
211             "",
212             "import dagger.Module;",
213             "import dagger.Provides;",
214             "",
215             "public class Maybe {",
216             "  @Module",
217             "  public static class MaybeModule {",
218             "    @Provides static Maybe provideMaybe() { return new Maybe(); }",
219             "  }",
220             "}");
221     JavaFileObject definitelyNot =
222         JavaFileObjects.forSourceLines(
223             "other.DefinitelyNot",
224             "package other;",
225             "",
226             "public class DefinitelyNot {}");
227 
228     JavaFileObject component =
229         JavaFileObjects.forSourceLines(
230             "test.TestComponent",
231             "package test;",
232             "",
233             "import com.google.common.base.Optional;",
234             "import com.google.common.util.concurrent.ListenableFuture;",
235             "import dagger.producers.ProductionComponent;",
236             "import javax.inject.Provider;",
237             "import other.Maybe;",
238             "import other.DefinitelyNot;",
239             "",
240             "@ProductionComponent(modules = {TestModule.class, Maybe.MaybeModule.class})",
241             "interface TestComponent {",
242             "  ListenableFuture<Optional<Maybe>> maybe();",
243             "  ListenableFuture<Optional<DefinitelyNot>> definitelyNot();",
244             "}");
245     JavaFileObject generatedComponent =
246         JavaFileObjects.forSourceLines(
247             "test.DaggerTestComponent",
248             "package test;",
249             "",
250             "import com.google.common.base.Optional;",
251             "import dagger.producers.internal.CancellationListener;",
252             "",
253             GENERATED_CODE_ANNOTATIONS,
254             "final class DaggerTestComponent implements TestComponent, CancellationListener {",
255             "  @Override",
256             "  public ListenableFuture<Optional<Maybe>> maybe() {",
257             "    return Futures.immediateFuture(",
258             "        Optional.of(Maybe_MaybeModule_ProvideMaybeFactory.provideMaybe()));",
259             "  }",
260             "",
261             "  @Override",
262             "  public ListenableFuture<Optional<DefinitelyNot>> definitelyNot() {",
263             "    return Futures.immediateFuture(Optional.<DefinitelyNot>absent());",
264             "  }",
265             "",
266             "  @Override",
267             "  public void onProducerFutureCancelled(boolean mayInterruptIfRunning) {}",
268             "}");
269 
270     Compilation compilation =
271         compilerWithOptions(
272                 compilerMode
273                 , CompilerMode.JAVA7
274                 )
275             .compile(module, maybe, definitelyNot, component);
276     assertThat(compilation).succeeded();
277     assertThat(compilation)
278         .generatedSourceFile("test.DaggerTestComponent")
279         .containsElementsIn(generatedComponent);
280   }
281 }
282