blob: b1407119462577607327d122b819791c3374ca6e [file] [log] [blame]
/*
* Copyright (C) 2022 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.functional.kotlinsrc.builder
import com.google.common.truth.Truth.assertThat
import dagger.Component
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import javax.inject.Inject
import javax.inject.Provider
import javax.inject.Scope
import javax.inject.Singleton
import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class BuilderTest {
@Subcomponent(
modules =
[
StringModule::class,
IntModuleIncludingDoubleAndFloat::class,
LongModule::class,
ByteModule::class
]
)
internal interface TestChildComponentWithBuilderAbstractClass {
fun s(): String
fun i(): Int
fun l(): Long
fun f(): Float
fun d(): Double
fun b(): Byte
abstract class SharedBuilder<B, C, M1, M2> {
abstract fun build(): C // Test resolving return type of build()
abstract fun setM1(m1: M1): B // Test resolving return type & param of setter
abstract fun setM2(m2: M2): SharedBuilder<B, C, M1, M2> // Test being overridden
abstract fun setM3(doubleModule: DoubleModule) // Test being overridden
abstract fun set(
floatModule: FloatModule
): SharedBuilder<B, C, M1, M2> // Test returning supertype.
}
@Subcomponent.Builder
abstract class Builder :
TestChildComponentWithBuilderAbstractClass.SharedBuilder<
Builder,
TestChildComponentWithBuilderAbstractClass,
StringModule,
IntModuleIncludingDoubleAndFloat
>() {
abstract override fun setM2(m2: IntModuleIncludingDoubleAndFloat): Builder // Test covariance
abstract override fun setM3(doubleModule: DoubleModule) // Test simple overrides allowed
abstract fun set(byteModule: ByteModule) // Note we're missing LongModule -- it's implicit
}
}
@Subcomponent(
modules =
[
StringModule::class,
IntModuleIncludingDoubleAndFloat::class,
LongModule::class,
ByteModule::class
]
)
internal interface TestChildComponentWithBuilderInterface {
fun s(): String
fun i(): Int
fun l(): Long
fun f(): Float
fun d(): Double
fun b(): Byte
interface SharedBuilder<B, C, M1, M2> {
fun build(): C // Test resolving return type of build()
fun setM1(m1: M1): B // Test resolving return type & param of setter
fun setM2(m2: M2): SharedBuilder<B, C, M1, M2> // Test being overridden
fun setM3(doubleModule: DoubleModule) // Test being overridden
fun set(
floatModule: FloatModule
): SharedBuilder<B, C, M1, M2> // Test return type is supertype.
}
@Subcomponent.Builder
interface Builder :
TestChildComponentWithBuilderInterface.SharedBuilder<
Builder,
TestChildComponentWithBuilderInterface,
StringModule,
IntModuleIncludingDoubleAndFloat
> {
override fun setM2(m2: IntModuleIncludingDoubleAndFloat): Builder // Test covariant overrides
override fun setM3(doubleModule: DoubleModule) // Test simple overrides allowed
fun set(byteModule: ByteModule) // Note we're missing LongModule -- it's implicit
}
}
@Component(
modules = [StringModule::class, IntModuleIncludingDoubleAndFloat::class, LongModule::class],
dependencies = [DepComponent::class]
)
internal abstract class TestComponentWithBuilderAbstractClass {
abstract fun s(): String
abstract fun i(): Int
abstract fun l(): Long
abstract fun f(): Float
abstract fun d(): Double
internal abstract class SharedBuilder {
// Make sure we use the overriding signature.
abstract fun build(): Any
open fun stringModule(stringModule: StringModule): Any? = null
@Suppress("UNUSED_PARAMETER")
fun ignoredLongModule(longModule: LongModule): SharedBuilder? = null
}
@Component.Builder
internal abstract class Builder : TestComponentWithBuilderAbstractClass.SharedBuilder() {
abstract override fun build(): TestComponentWithBuilderAbstractClass // Narrowing return type
abstract override fun stringModule(stringModule: StringModule): Builder // Abstract & narrow
abstract fun intModule(intModule: IntModuleIncludingDoubleAndFloat): Builder
abstract fun doubleModule(doubleModule: DoubleModule) // Module w/o args
abstract fun depComponent(depComponent: DepComponent)
// Note we're missing LongModule & FloatModule -- they/re implicit
@Suppress("UNUSED_PARAMETER")
fun ignoredIntModule(intModule: IntModuleIncludingDoubleAndFloat): Builder? = null
}
companion object {
fun builder(): Builder = DaggerBuilderTest_TestComponentWithBuilderAbstractClass.builder()
}
}
@Component(
modules = [StringModule::class, IntModuleIncludingDoubleAndFloat::class, LongModule::class],
dependencies = [DepComponent::class]
)
internal interface TestComponentWithBuilderInterface {
fun s(): String
fun i(): Int
fun l(): Long
fun f(): Float
fun d(): Double
interface SharedBuilder {
// Make sure we use the overriding signature.
fun build(): Any
fun stringModule(m1: StringModule): Any
}
@Component.Builder
interface Builder : TestComponentWithBuilderInterface.SharedBuilder {
override fun build(): TestComponentWithBuilderInterface // Narrowing return type
override fun stringModule(m1: StringModule): Builder // Narrowing return type
fun intModule(intModule: IntModuleIncludingDoubleAndFloat): Builder
fun doubleModule(doubleModule: DoubleModule) // Module w/o args
fun depComponent(
depComponent: DepComponent
) // Note we're missing LongModule & FloatModule -- they/re implicit
}
}
@Component(
modules = [StringModule::class, IntModuleIncludingDoubleAndFloat::class, LongModule::class],
dependencies = [DepComponent::class]
)
internal interface TestComponentWithGenericBuilderAbstractClass {
fun s(): String
fun i(): Int
fun l(): Long
fun f(): Float
fun d(): Double
abstract class SharedBuilder<B, C, M1, M2> {
abstract fun build(): C // Test resolving return type of build()
abstract fun setM1(m1: M1): B // Test resolving return type & param of setter
abstract fun setM2(m2: M2): SharedBuilder<B, C, M1, M2> // Test being overridden
abstract fun doubleModule(doubleModule: DoubleModule) // Test being overridden
abstract fun depComponent(
floatModule: FloatModule
): SharedBuilder<B, C, M1, M2> // Test return type
}
@Component.Builder
abstract class Builder :
TestComponentWithGenericBuilderAbstractClass.SharedBuilder<
Builder,
TestComponentWithGenericBuilderAbstractClass,
StringModule,
IntModuleIncludingDoubleAndFloat
>() {
abstract override fun setM2(
m2: IntModuleIncludingDoubleAndFloat
): Builder // Test covariant overrides
abstract override fun doubleModule(
doubleModule: DoubleModule
) // Test simple overrides allowed
abstract fun depComponent(
depComponent: DepComponent
) // Note we're missing LongModule & FloatModule -- they're implicit
}
}
@Component(
modules = [StringModule::class, IntModuleIncludingDoubleAndFloat::class, LongModule::class],
dependencies = [DepComponent::class]
)
internal interface TestComponentWithGenericBuilderInterface {
fun s(): String
fun i(): Int
fun l(): Long
fun f(): Float
fun d(): Double
interface SharedBuilder<B, C, M1, M2> {
fun build(): C // Test resolving return type of build()
fun setM1(m1: M1): B // Test resolving return type & param of setter
fun setM2(m2: M2): SharedBuilder<B, C, M1, M2> // Test being overridden
fun doubleModule(doubleModule: DoubleModule) // Test being overridden
fun set(
floatModule: FloatModule
): SharedBuilder<B, C, M1, M2> // Test return type is supertype.
}
@Component.Builder
interface Builder :
TestComponentWithGenericBuilderInterface.SharedBuilder<
Builder,
TestComponentWithGenericBuilderInterface,
StringModule,
IntModuleIncludingDoubleAndFloat
> {
override fun setM2(
m2: IntModuleIncludingDoubleAndFloat
): Builder // Test covariant overrides allowed
override fun doubleModule(doubleModule: DoubleModule) // Test simple overrides allowed
fun depComponent(depComponent: DepComponent) // Note we're missing M5 -- that's implicit.
}
}
@Component internal interface DepComponent
@Singleton
@Component
internal interface ParentComponent {
fun childAbstractClassBuilder(): TestChildComponentWithBuilderAbstractClass.Builder
fun childInterfaceBuilder(): TestChildComponentWithBuilderInterface.Builder
fun middleBuilder(): MiddleChild.Builder
fun otherBuilder(): OtherMiddleChild.Builder
fun requiresMiddleChildBuilder(): RequiresSubcomponentBuilder<MiddleChild.Builder>
}
@Scope internal annotation class MiddleScope
@MiddleScope
@Subcomponent(modules = [StringModule::class])
internal interface MiddleChild {
fun s(): String
fun grandchildBuilder(): Grandchild.Builder
fun requiresGrandchildBuilder(): RequiresSubcomponentBuilder<Grandchild.Builder>
@Subcomponent.Builder
interface Builder {
fun build(): MiddleChild
fun set(stringModule: StringModule): Builder
}
}
internal class RequiresSubcomponentBuilder<B>
@Inject
constructor(
private val subcomponentBuilderProvider: Provider<B>,
private val subcomponentBuilder: B
) {
fun subcomponentBuilderProvider() = subcomponentBuilderProvider
fun subcomponentBuilder() = subcomponentBuilder
}
@MiddleScope
@Subcomponent(modules = [StringModule::class, LongModule::class])
internal interface OtherMiddleChild {
fun l(): Long
fun s(): String
fun grandchildBuilder(): Grandchild.Builder
@Subcomponent.Builder
interface Builder {
fun build(): OtherMiddleChild
fun set(stringModule: StringModule): Builder
}
}
@Component(modules = [StringModule::class])
@Singleton
internal interface ParentOfGenericComponent : GenericParent<Grandchild.Builder>
@Subcomponent(modules = [IntModuleIncludingDoubleAndFloat::class])
internal interface Grandchild {
fun i(): Int
fun s(): String
@Subcomponent.Builder
interface Builder {
fun build(): Grandchild
fun set(intModule: IntModuleIncludingDoubleAndFloat): Builder
}
}
internal interface GenericParent<B> {
fun subcomponentBuilder(): B
}
@Module
internal class ByteModule(private val b: Byte) {
@Provides fun b(): Byte = b
}
@Module
internal class DoubleModule {
@Provides fun d(): Double = 4.2
}
@Module
internal class LongModule {
@Provides fun l(): Long = 6L
}
@Module
internal class FloatModule {
@Provides fun f(): Float = 5.5f
}
@Module
internal class StringModule(private val string: String) {
@Provides fun string(): String = string
}
@Module(includes = [DoubleModule::class, FloatModule::class])
internal class IntModuleIncludingDoubleAndFloat(private val integer: Int) {
@Provides fun integer(): Int = integer
}
@Test
fun interfaceBuilder() {
val builder = DaggerBuilderTest_TestComponentWithBuilderInterface.builder()
// Make sure things fail if we don't set our required modules.
try {
builder.build()
fail()
} catch (expected: IllegalStateException) {}
builder
.intModule(IntModuleIncludingDoubleAndFloat(1))
.stringModule(StringModule("sam"))
.depComponent(object : DepComponent {})
builder.doubleModule(DoubleModule())
// Don't set other modules -- make sure it works.
val component = builder.build()
assertThat(component.s()).isEqualTo("sam")
assertThat(component.i()).isEqualTo(1)
assertThat(component.d()).isEqualTo(4.2)
assertThat(component.f()).isEqualTo(5.5f)
assertThat(component.l()).isEqualTo(6L)
}
@Test
fun abstractClassBuilder() {
val builder = TestComponentWithBuilderAbstractClass.builder()
// Make sure things fail if we don't set our required modules.
try {
builder.build()
fail()
} catch (expected: IllegalStateException) {}
builder
.intModule(IntModuleIncludingDoubleAndFloat(1))
.stringModule(StringModule("sam"))
.depComponent(object : DepComponent {})
builder.doubleModule(DoubleModule())
// Don't set other modules -- make sure it works.
val component = builder.build()
assertThat(component.s()).isEqualTo("sam")
assertThat(component.i()).isEqualTo(1)
assertThat(component.d()).isEqualTo(4.2)
assertThat(component.f()).isEqualTo(5.5f)
assertThat(component.l()).isEqualTo(6L)
}
@Test
fun interfaceGenericBuilder() {
val builder = DaggerBuilderTest_TestComponentWithGenericBuilderInterface.builder()
// Make sure things fail if we don't set our required modules.
try {
builder.build()
fail()
} catch (expected: IllegalStateException) {}
builder
.setM2(IntModuleIncludingDoubleAndFloat(1))
.setM1(StringModule("sam"))
.depComponent(object : DepComponent {})
builder.doubleModule(DoubleModule())
// Don't set other modules -- make sure it works.
val component = builder.build()
assertThat(component.s()).isEqualTo("sam")
assertThat(component.i()).isEqualTo(1)
assertThat(component.d()).isEqualTo(4.2)
assertThat(component.f()).isEqualTo(5.5f)
assertThat(component.l()).isEqualTo(6L)
}
@Test
fun abstractClassGenericBuilder() {
val builder = DaggerBuilderTest_TestComponentWithGenericBuilderAbstractClass.builder()
// Make sure things fail if we don't set our required modules.
try {
builder.build()
fail()
} catch (expected: IllegalStateException) {}
builder
.setM2(IntModuleIncludingDoubleAndFloat(1))
.setM1(StringModule("sam"))
.depComponent(object : DepComponent {})
builder.doubleModule(DoubleModule())
// Don't set other modules -- make sure it works.
val component = builder.build()
assertThat(component.s()).isEqualTo("sam")
assertThat(component.i()).isEqualTo(1)
assertThat(component.d()).isEqualTo(4.2)
assertThat(component.f()).isEqualTo(5.5f)
assertThat(component.l()).isEqualTo(6L)
}
@Test
fun subcomponents_interface() {
val parent = DaggerBuilderTest_ParentComponent.create()
val builder1 = parent.childInterfaceBuilder()
try {
builder1.build()
fail()
} catch (expected: IllegalStateException) {}
builder1
.setM2(IntModuleIncludingDoubleAndFloat(1))
.setM1(StringModule("sam"))
.set(ByteModule(7.toByte()))
builder1.set(FloatModule())
val child1 = builder1.build()
assertThat(child1.s()).isEqualTo("sam")
assertThat(child1.i()).isEqualTo(1)
assertThat(child1.d()).isEqualTo(4.2)
assertThat(child1.f()).isEqualTo(5.5f)
assertThat(child1.l()).isEqualTo(6L)
assertThat(child1.b()).isEqualTo(7.toByte())
}
@Test
fun subcomponents_abstractclass() {
val parent = DaggerBuilderTest_ParentComponent.create()
val builder2 = parent.childAbstractClassBuilder()
try {
builder2.build()
fail()
} catch (expected: IllegalStateException) {}
builder2
.setM2(IntModuleIncludingDoubleAndFloat(10))
.setM1(StringModule("tara"))
.set(ByteModule(70.toByte()))
builder2.set(FloatModule())
val child2 = builder2.build()
assertThat(child2.s()).isEqualTo("tara")
assertThat(child2.i()).isEqualTo(10)
assertThat(child2.d()).isEqualTo(4.2)
assertThat(child2.f()).isEqualTo(5.5f)
assertThat(child2.l()).isEqualTo(6L)
assertThat(child2.b()).isEqualTo(70.toByte())
}
@Test
fun grandchildren() {
val parent = DaggerBuilderTest_ParentComponent.create()
val middle1 = parent.middleBuilder().set(StringModule("sam")).build()
val grandchild1 = middle1.grandchildBuilder().set(IntModuleIncludingDoubleAndFloat(21)).build()
val grandchild2 = middle1.grandchildBuilder().set(IntModuleIncludingDoubleAndFloat(22)).build()
assertThat(middle1.s()).isEqualTo("sam")
assertThat(grandchild1.i()).isEqualTo(21)
assertThat(grandchild1.s()).isEqualTo("sam")
assertThat(grandchild2.i()).isEqualTo(22)
assertThat(grandchild2.s()).isEqualTo("sam")
// Make sure grandchildren from newer children have no relation to the older ones.
val middle2 = parent.middleBuilder().set(StringModule("tara")).build()
val grandchild3 = middle2.grandchildBuilder().set(IntModuleIncludingDoubleAndFloat(23)).build()
val grandchild4 = middle2.grandchildBuilder().set(IntModuleIncludingDoubleAndFloat(24)).build()
assertThat(middle2.s()).isEqualTo("tara")
assertThat(grandchild3.i()).isEqualTo(23)
assertThat(grandchild3.s()).isEqualTo("tara")
assertThat(grandchild4.i()).isEqualTo(24)
assertThat(grandchild4.s()).isEqualTo("tara")
}
@Test
fun diamondGrandchildren() {
val parent = DaggerBuilderTest_ParentComponent.create()
val middle = parent.middleBuilder().set(StringModule("sam")).build()
val other = parent.otherBuilder().set(StringModule("tara")).build()
val middlegrand = middle.grandchildBuilder().set(IntModuleIncludingDoubleAndFloat(21)).build()
val othergrand = other.grandchildBuilder().set(IntModuleIncludingDoubleAndFloat(22)).build()
assertThat(middle.s()).isEqualTo("sam")
assertThat(other.s()).isEqualTo("tara")
assertThat(middlegrand.s()).isEqualTo("sam")
assertThat(othergrand.s()).isEqualTo("tara")
assertThat(middlegrand.i()).isEqualTo(21)
assertThat(othergrand.i()).isEqualTo(22)
}
@Test
fun genericSubcomponentMethod() {
val parent =
DaggerBuilderTest_ParentOfGenericComponent.builder().stringModule(StringModule("sam")).build()
val builder = parent.subcomponentBuilder()
val child = builder.set(IntModuleIncludingDoubleAndFloat(21)).build()
assertThat(child.s()).isEqualTo("sam")
assertThat(child.i()).isEqualTo(21)
}
@Test
fun requireSubcomponentBuilderProviders() {
val parent = DaggerBuilderTest_ParentComponent.create()
val middle =
parent
.requiresMiddleChildBuilder()
.subcomponentBuilderProvider()
.get()
.set(StringModule("sam"))
.build()
val grandchild =
middle
.requiresGrandchildBuilder()
.subcomponentBuilderProvider()
.get()
.set(IntModuleIncludingDoubleAndFloat(12))
.build()
assertThat(middle.s()).isEqualTo("sam")
assertThat(grandchild.i()).isEqualTo(12)
assertThat(grandchild.s()).isEqualTo("sam")
}
@Test
fun requireSubcomponentBuilders() {
val parent = DaggerBuilderTest_ParentComponent.create()
val middle =
parent.requiresMiddleChildBuilder().subcomponentBuilder().set(StringModule("sam")).build()
val grandchild =
middle
.requiresGrandchildBuilder()
.subcomponentBuilder()
.set(IntModuleIncludingDoubleAndFloat(12))
.build()
assertThat(middle.s()).isEqualTo("sam")
assertThat(grandchild.i()).isEqualTo(12)
assertThat(grandchild.s()).isEqualTo("sam")
}
}