600 lines
25 KiB
Java
600 lines
25 KiB
Java
/*
|
|
* Copyright (C) 2014 The Dagger Authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package dagger.internal.codegen;
|
|
|
|
import static com.google.testing.compile.CompilationSubject.assertThat;
|
|
import static com.google.testing.compile.Compiler.javac;
|
|
|
|
import com.google.common.collect.ImmutableList;
|
|
import com.google.testing.compile.Compilation;
|
|
import com.google.testing.compile.Compiler;
|
|
import com.google.testing.compile.JavaFileObjects;
|
|
import javax.tools.JavaFileObject;
|
|
import org.junit.Test;
|
|
import org.junit.runner.RunWith;
|
|
import org.junit.runners.JUnit4;
|
|
|
|
@RunWith(JUnit4.class)
|
|
public class SwitchingProviderTest {
|
|
@Test
|
|
public void switchingProviderTest() {
|
|
ImmutableList.Builder<JavaFileObject> javaFileObjects = ImmutableList.builder();
|
|
StringBuilder entryPoints = new StringBuilder();
|
|
for (int i = 0; i <= 100; i++) {
|
|
String bindingName = "Binding" + i;
|
|
javaFileObjects.add(
|
|
JavaFileObjects.forSourceLines(
|
|
"test." + bindingName,
|
|
"package test;",
|
|
"",
|
|
"import javax.inject.Inject;",
|
|
"",
|
|
"final class " + bindingName + " {",
|
|
" @Inject",
|
|
" " + bindingName + "() {}",
|
|
"}"));
|
|
entryPoints.append(String.format(" Provider<%1$s> get%1$sProvider();\n", bindingName));
|
|
}
|
|
|
|
javaFileObjects.add(
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Provider;",
|
|
"",
|
|
"@Component",
|
|
"interface TestComponent {",
|
|
entryPoints.toString(),
|
|
"}"));
|
|
|
|
JavaFileObject generatedComponent =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.DaggerTestComponent",
|
|
"package test;",
|
|
GeneratedLines.generatedAnnotations(),
|
|
"final class DaggerTestComponent implements TestComponent {",
|
|
" private final class SwitchingProvider<T> implements Provider<T> {",
|
|
" @SuppressWarnings(\"unchecked\")",
|
|
" private T get0() {",
|
|
" switch (id) {",
|
|
" case 0: return (T) new Binding0();",
|
|
" case 1: return (T) new Binding1();",
|
|
" case 2: return (T) new Binding2();",
|
|
" case 3: return (T) new Binding3();",
|
|
" case 4: return (T) new Binding4();",
|
|
" case 5: return (T) new Binding5();",
|
|
" case 6: return (T) new Binding6();",
|
|
" case 7: return (T) new Binding7();",
|
|
" case 8: return (T) new Binding8();",
|
|
" case 9: return (T) new Binding9();",
|
|
" case 10: return (T) new Binding10();",
|
|
" case 11: return (T) new Binding11();",
|
|
" case 12: return (T) new Binding12();",
|
|
" case 13: return (T) new Binding13();",
|
|
" case 14: return (T) new Binding14();",
|
|
" case 15: return (T) new Binding15();",
|
|
" case 16: return (T) new Binding16();",
|
|
" case 17: return (T) new Binding17();",
|
|
" case 18: return (T) new Binding18();",
|
|
" case 19: return (T) new Binding19();",
|
|
" case 20: return (T) new Binding20();",
|
|
" case 21: return (T) new Binding21();",
|
|
" case 22: return (T) new Binding22();",
|
|
" case 23: return (T) new Binding23();",
|
|
" case 24: return (T) new Binding24();",
|
|
" case 25: return (T) new Binding25();",
|
|
" case 26: return (T) new Binding26();",
|
|
" case 27: return (T) new Binding27();",
|
|
" case 28: return (T) new Binding28();",
|
|
" case 29: return (T) new Binding29();",
|
|
" case 30: return (T) new Binding30();",
|
|
" case 31: return (T) new Binding31();",
|
|
" case 32: return (T) new Binding32();",
|
|
" case 33: return (T) new Binding33();",
|
|
" case 34: return (T) new Binding34();",
|
|
" case 35: return (T) new Binding35();",
|
|
" case 36: return (T) new Binding36();",
|
|
" case 37: return (T) new Binding37();",
|
|
" case 38: return (T) new Binding38();",
|
|
" case 39: return (T) new Binding39();",
|
|
" case 40: return (T) new Binding40();",
|
|
" case 41: return (T) new Binding41();",
|
|
" case 42: return (T) new Binding42();",
|
|
" case 43: return (T) new Binding43();",
|
|
" case 44: return (T) new Binding44();",
|
|
" case 45: return (T) new Binding45();",
|
|
" case 46: return (T) new Binding46();",
|
|
" case 47: return (T) new Binding47();",
|
|
" case 48: return (T) new Binding48();",
|
|
" case 49: return (T) new Binding49();",
|
|
" case 50: return (T) new Binding50();",
|
|
" case 51: return (T) new Binding51();",
|
|
" case 52: return (T) new Binding52();",
|
|
" case 53: return (T) new Binding53();",
|
|
" case 54: return (T) new Binding54();",
|
|
" case 55: return (T) new Binding55();",
|
|
" case 56: return (T) new Binding56();",
|
|
" case 57: return (T) new Binding57();",
|
|
" case 58: return (T) new Binding58();",
|
|
" case 59: return (T) new Binding59();",
|
|
" case 60: return (T) new Binding60();",
|
|
" case 61: return (T) new Binding61();",
|
|
" case 62: return (T) new Binding62();",
|
|
" case 63: return (T) new Binding63();",
|
|
" case 64: return (T) new Binding64();",
|
|
" case 65: return (T) new Binding65();",
|
|
" case 66: return (T) new Binding66();",
|
|
" case 67: return (T) new Binding67();",
|
|
" case 68: return (T) new Binding68();",
|
|
" case 69: return (T) new Binding69();",
|
|
" case 70: return (T) new Binding70();",
|
|
" case 71: return (T) new Binding71();",
|
|
" case 72: return (T) new Binding72();",
|
|
" case 73: return (T) new Binding73();",
|
|
" case 74: return (T) new Binding74();",
|
|
" case 75: return (T) new Binding75();",
|
|
" case 76: return (T) new Binding76();",
|
|
" case 77: return (T) new Binding77();",
|
|
" case 78: return (T) new Binding78();",
|
|
" case 79: return (T) new Binding79();",
|
|
" case 80: return (T) new Binding80();",
|
|
" case 81: return (T) new Binding81();",
|
|
" case 82: return (T) new Binding82();",
|
|
" case 83: return (T) new Binding83();",
|
|
" case 84: return (T) new Binding84();",
|
|
" case 85: return (T) new Binding85();",
|
|
" case 86: return (T) new Binding86();",
|
|
" case 87: return (T) new Binding87();",
|
|
" case 88: return (T) new Binding88();",
|
|
" case 89: return (T) new Binding89();",
|
|
" case 90: return (T) new Binding90();",
|
|
" case 91: return (T) new Binding91();",
|
|
" case 92: return (T) new Binding92();",
|
|
" case 93: return (T) new Binding93();",
|
|
" case 94: return (T) new Binding94();",
|
|
" case 95: return (T) new Binding95();",
|
|
" case 96: return (T) new Binding96();",
|
|
" case 97: return (T) new Binding97();",
|
|
" case 98: return (T) new Binding98();",
|
|
" case 99: return (T) new Binding99();",
|
|
" default: throw new AssertionError(id);",
|
|
" }",
|
|
" }",
|
|
"",
|
|
" @SuppressWarnings(\"unchecked\")",
|
|
" private T get1() {",
|
|
" switch (id) {",
|
|
" case 100: return (T) new Binding100();",
|
|
" default: throw new AssertionError(id);",
|
|
" }",
|
|
" }",
|
|
"",
|
|
" @Override",
|
|
" public T get() {",
|
|
" switch (id / 100) {",
|
|
" case 0: return get0();",
|
|
" case 1: return get1();",
|
|
" default: throw new AssertionError(id);",
|
|
" }",
|
|
" }",
|
|
" }",
|
|
"}");
|
|
|
|
Compilation compilation = compilerWithAndroidMode().compile(javaFileObjects.build());
|
|
assertThat(compilation).succeededWithoutWarnings();
|
|
assertThat(compilation)
|
|
.generatedSourceFile("test.DaggerTestComponent")
|
|
.containsElementsIn(generatedComponent);
|
|
}
|
|
|
|
@Test
|
|
public void unscopedBinds() {
|
|
JavaFileObject module =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Binds;",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"",
|
|
"@Module",
|
|
"interface TestModule {",
|
|
" @Provides",
|
|
" static String s() {",
|
|
" return new String();",
|
|
" }",
|
|
"",
|
|
" @Binds CharSequence c(String s);",
|
|
" @Binds Object o(CharSequence c);",
|
|
"}");
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Provider;",
|
|
"",
|
|
"@Component(modules = TestModule.class)",
|
|
"interface TestComponent {",
|
|
" Provider<Object> objectProvider();",
|
|
" Provider<CharSequence> charSequenceProvider();",
|
|
"}");
|
|
|
|
Compilation compilation = compilerWithAndroidMode().compile(module, component);
|
|
assertThat(compilation).succeeded();
|
|
assertThat(compilation)
|
|
.generatedSourceFile("test.DaggerTestComponent")
|
|
.containsElementsIn(
|
|
JavaFileObjects.forSourceLines(
|
|
"test.DaggerTestComponent",
|
|
"package test;",
|
|
"",
|
|
GeneratedLines.generatedAnnotations(),
|
|
"final class DaggerTestComponent implements TestComponent {",
|
|
" private volatile Provider<String> sProvider;",
|
|
"",
|
|
" private Provider<String> stringProvider() {",
|
|
" Object local = sProvider;",
|
|
" if (local == null) {",
|
|
" local = new SwitchingProvider<>(0);",
|
|
" sProvider = (Provider<String>) local;",
|
|
" }",
|
|
" return (Provider<String>) local;",
|
|
" }",
|
|
"",
|
|
" @Override",
|
|
" public Provider<Object> objectProvider() {",
|
|
" return (Provider) stringProvider();",
|
|
" }",
|
|
"",
|
|
" @Override",
|
|
" public Provider<CharSequence> charSequenceProvider() {",
|
|
" return (Provider) stringProvider();",
|
|
" }",
|
|
"",
|
|
" private final class SwitchingProvider<T> implements Provider<T> {",
|
|
" @SuppressWarnings(\"unchecked\")",
|
|
" @Override",
|
|
" public T get() {",
|
|
" switch (id) {",
|
|
" case 0:",
|
|
" return (T) TestModule_SFactory.s();",
|
|
" default:",
|
|
" throw new AssertionError(id);",
|
|
" }",
|
|
" }",
|
|
" }",
|
|
"}"));
|
|
}
|
|
|
|
@Test
|
|
public void scopedBinds() {
|
|
JavaFileObject module =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Binds;",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Module",
|
|
"interface TestModule {",
|
|
" @Provides",
|
|
" static String s() {",
|
|
" return new String();",
|
|
" }",
|
|
"",
|
|
" @Binds @Singleton Object o(CharSequence s);",
|
|
" @Binds @Singleton CharSequence c(String s);",
|
|
"}");
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import javax.inject.Provider;",
|
|
"import javax.inject.Singleton;",
|
|
"",
|
|
"@Singleton",
|
|
"@Component(modules = TestModule.class)",
|
|
"interface TestComponent {",
|
|
" Provider<Object> objectProvider();",
|
|
" Provider<CharSequence> charSequenceProvider();",
|
|
"}");
|
|
|
|
Compilation compilation = compilerWithAndroidMode().compile(module, component);
|
|
assertThat(compilation).succeeded();
|
|
assertThat(compilation)
|
|
.generatedSourceFile("test.DaggerTestComponent")
|
|
.containsElementsIn(
|
|
JavaFileObjects.forSourceLines(
|
|
"test.DaggerTestComponent",
|
|
"package test;",
|
|
"",
|
|
GeneratedLines.generatedAnnotations(),
|
|
"final class DaggerTestComponent implements TestComponent {",
|
|
" private volatile Object charSequence = new MemoizedSentinel();",
|
|
" private volatile Provider<CharSequence> cProvider;",
|
|
"",
|
|
" private CharSequence charSequence() {",
|
|
" Object local = charSequence;",
|
|
" if (local instanceof MemoizedSentinel) {",
|
|
" synchronized (local) {",
|
|
" local = charSequence;",
|
|
" if (local instanceof MemoizedSentinel) {",
|
|
" local = TestModule_SFactory.s();",
|
|
" charSequence = DoubleCheck.reentrantCheck(charSequence, local);",
|
|
" }",
|
|
" }",
|
|
" }",
|
|
" return (CharSequence) local;",
|
|
" }",
|
|
"",
|
|
" @Override",
|
|
" public Provider<Object> objectProvider() {",
|
|
" return (Provider) charSequenceProvider();",
|
|
" }",
|
|
"",
|
|
" @Override",
|
|
" public Provider<CharSequence> charSequenceProvider() {",
|
|
" Object local = cProvider;",
|
|
" if (local == null) {",
|
|
" local = new SwitchingProvider<>(0);",
|
|
" cProvider = (Provider<CharSequence>) local;",
|
|
" }",
|
|
" return (Provider<CharSequence>) local;",
|
|
" }",
|
|
"",
|
|
" private final class SwitchingProvider<T> implements Provider<T> {",
|
|
" @SuppressWarnings(\"unchecked\")",
|
|
" @Override",
|
|
" public T get() {",
|
|
" switch (id) {",
|
|
" case 0:",
|
|
" return (T) DaggerTestComponent.this.charSequence();",
|
|
" default:",
|
|
" throw new AssertionError(id);",
|
|
" }",
|
|
" }",
|
|
" }",
|
|
"}"));
|
|
}
|
|
|
|
@Test
|
|
public void emptyMultibindings_avoidSwitchProviders() {
|
|
JavaFileObject module =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.multibindings.Multibinds;",
|
|
"import dagger.Module;",
|
|
"import java.util.Map;",
|
|
"import java.util.Set;",
|
|
"",
|
|
"@Module",
|
|
"interface TestModule {",
|
|
" @Multibinds Set<String> set();",
|
|
" @Multibinds Map<String, String> map();",
|
|
"}");
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import java.util.Map;",
|
|
"import java.util.Set;",
|
|
"import javax.inject.Provider;",
|
|
"",
|
|
"@Component(modules = TestModule.class)",
|
|
"interface TestComponent {",
|
|
" Provider<Set<String>> setProvider();",
|
|
" Provider<Map<String, String>> mapProvider();",
|
|
"}");
|
|
|
|
Compilation compilation = compilerWithAndroidMode().compile(module, component);
|
|
assertThat(compilation).succeeded();
|
|
assertThat(compilation)
|
|
.generatedSourceFile("test.DaggerTestComponent")
|
|
.containsElementsIn(
|
|
JavaFileObjects.forSourceLines(
|
|
"test.DaggerTestComponent",
|
|
"package test;",
|
|
"",
|
|
GeneratedLines.generatedAnnotations(),
|
|
"final class DaggerTestComponent implements TestComponent {",
|
|
" @Override",
|
|
" public Provider<Set<String>> setProvider() {",
|
|
" return SetFactory.<String>empty();",
|
|
" }",
|
|
"",
|
|
" @Override",
|
|
" public Provider<Map<String, String>> mapProvider() {",
|
|
" return MapFactory.<String, String>emptyMapProvider();",
|
|
" }",
|
|
"}"));
|
|
}
|
|
|
|
@Test
|
|
public void memberInjectors() {
|
|
JavaFileObject foo =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.Foo",
|
|
"package test;",
|
|
"",
|
|
"class Foo {}");
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import dagger.MembersInjector;",
|
|
"import javax.inject.Provider;",
|
|
"",
|
|
"@Component",
|
|
"interface TestComponent {",
|
|
" Provider<MembersInjector<Foo>> providerOfMembersInjector();",
|
|
"}");
|
|
|
|
Compilation compilation = compilerWithAndroidMode().compile(foo, component);
|
|
assertThat(compilation).succeeded();
|
|
assertThat(compilation)
|
|
.generatedSourceFile("test.DaggerTestComponent")
|
|
.containsElementsIn(
|
|
JavaFileObjects.forSourceLines(
|
|
"test.DaggerTestComponent",
|
|
"package test;",
|
|
"",
|
|
GeneratedLines.generatedAnnotations(),
|
|
"final class DaggerTestComponent implements TestComponent {",
|
|
" private Provider<MembersInjector<Foo>> fooMembersInjectorProvider;",
|
|
"",
|
|
" @SuppressWarnings(\"unchecked\")",
|
|
" private void initialize() {",
|
|
" this.fooMembersInjectorProvider = ",
|
|
" InstanceFactory.create(MembersInjectors.<Foo>noOp());",
|
|
" }",
|
|
"",
|
|
" @Override",
|
|
" public Provider<MembersInjector<Foo>> providerOfMembersInjector() {",
|
|
" return fooMembersInjectorProvider;",
|
|
" }",
|
|
"}"));
|
|
}
|
|
|
|
@Test
|
|
public void optionals() {
|
|
JavaFileObject present =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.Present",
|
|
"package test;",
|
|
"",
|
|
"class Present {}");
|
|
JavaFileObject absent =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.Absent",
|
|
"package test;",
|
|
"",
|
|
"class Absent {}");
|
|
JavaFileObject module =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestModule",
|
|
"package test;",
|
|
"",
|
|
"import dagger.BindsOptionalOf;",
|
|
"import dagger.Module;",
|
|
"import dagger.Provides;",
|
|
"",
|
|
"@Module",
|
|
"interface TestModule {",
|
|
" @BindsOptionalOf Present bindOptionalOfPresent();",
|
|
" @BindsOptionalOf Absent bindOptionalOfAbsent();",
|
|
"",
|
|
" @Provides static Present p() { return new Present(); }",
|
|
"}");
|
|
JavaFileObject component =
|
|
JavaFileObjects.forSourceLines(
|
|
"test.TestComponent",
|
|
"package test;",
|
|
"",
|
|
"import dagger.Component;",
|
|
"import java.util.Optional;",
|
|
"import javax.inject.Provider;",
|
|
"",
|
|
"@Component(modules = TestModule.class)",
|
|
"interface TestComponent {",
|
|
" Provider<Optional<Present>> providerOfOptionalOfPresent();",
|
|
" Provider<Optional<Absent>> providerOfOptionalOfAbsent();",
|
|
"}");
|
|
|
|
Compilation compilation = compilerWithAndroidMode().compile(present, absent, module, component);
|
|
assertThat(compilation).succeeded();
|
|
assertThat(compilation)
|
|
.generatedSourceFile("test.DaggerTestComponent")
|
|
.containsElementsIn(
|
|
JavaFileObjects.forSourceLines(
|
|
"test.DaggerTestComponent",
|
|
"package test;",
|
|
"",
|
|
GeneratedLines.generatedAnnotations(),
|
|
"final class DaggerTestComponent implements TestComponent {",
|
|
" @SuppressWarnings(\"rawtypes\")",
|
|
" private static final Provider ABSENT_JDK_OPTIONAL_PROVIDER =",
|
|
" InstanceFactory.create(Optional.empty());",
|
|
"",
|
|
" private volatile Provider<Optional<Present>> optionalOfPresentProvider;",
|
|
"",
|
|
" private Provider<Optional<Absent>> optionalOfAbsentProvider;",
|
|
"",
|
|
" @SuppressWarnings(\"unchecked\")",
|
|
" private void initialize() {",
|
|
" this.optionalOfAbsentProvider = absentJdkOptionalProvider();",
|
|
" }",
|
|
"",
|
|
" @Override",
|
|
" public Provider<Optional<Present>> providerOfOptionalOfPresent() {",
|
|
" Object local = optionalOfPresentProvider;",
|
|
" if (local == null) {",
|
|
" local = new SwitchingProvider<>(0);",
|
|
" optionalOfPresentProvider = (Provider<Optional<Present>>) local;",
|
|
" }",
|
|
" return (Provider<Optional<Present>>) local;",
|
|
" }",
|
|
"",
|
|
" @Override",
|
|
" public Provider<Optional<Absent>> providerOfOptionalOfAbsent() {",
|
|
" return optionalOfAbsentProvider;",
|
|
" }",
|
|
"",
|
|
" private static <T> Provider<Optional<T>> absentJdkOptionalProvider() {",
|
|
" @SuppressWarnings(\"unchecked\")",
|
|
" Provider<Optional<T>> provider = ",
|
|
" (Provider<Optional<T>>) ABSENT_JDK_OPTIONAL_PROVIDER;",
|
|
" return provider;",
|
|
" }",
|
|
"",
|
|
" private final class SwitchingProvider<T> implements Provider<T> {",
|
|
" @SuppressWarnings(\"unchecked\")",
|
|
" @Override",
|
|
" public T get() {",
|
|
" switch (id) {",
|
|
" case 0: // java.util.Optional<test.Present>",
|
|
" return (T) Optional.of(TestModule_PFactory.p());",
|
|
" default:",
|
|
" throw new AssertionError(id);",
|
|
" }",
|
|
" }",
|
|
" }",
|
|
"}"));
|
|
}
|
|
|
|
private Compiler compilerWithAndroidMode() {
|
|
return javac()
|
|
.withProcessors(new ComponentProcessor())
|
|
.withOptions(CompilerMode.FAST_INIT_MODE.javacopts());
|
|
}
|
|
}
|