Exclude Kotlin extensions for now since the build setup does not support it yet.

PiperOrigin-RevId: 432524021
This commit is contained in:
Guice Team
2022-03-04 13:50:22 -08:00
committed by Guice Team
parent bb59fbfe99
commit d65df48ba5
8 changed files with 0 additions and 1097 deletions
@@ -1,28 +0,0 @@
package com.google.inject
import com.google.inject.binder.AnnotatedBindingBuilder
import com.google.inject.binder.LinkedBindingBuilder
import kotlin.reflect.KClass
/**
* A wrapper of [AnnotatedBindingBuilder] that provides convenience functions when using
* [KAbstractModule].
*/
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
class ExtendedAnnotatedBindingBuilder<T>
internal constructor(private val delegate: AnnotatedBindingBuilder<T>) :
ExtendedLinkedBindingBuilder<T>(delegate), AnnotatedBindingBuilder<T> by delegate {
/**
* Returns a [LinkedBindingBuilder] of [T] where the bound key is annotated with the given
* annotation class.
*
* Usage:
* `bind<...>().annotatedWith(MyAnnotation::class).to<...>()`
*
* @param annotation the annotation to annotate [T]
* @sample [KAbstractModuleTest.testExtendedAnnotatedBindingBuilder_annotatedWith]
*/
fun <A : Annotation> annotatedWith(annotation: KClass<A>): LinkedBindingBuilder<T> =
annotatedWith(annotation.java)
}
@@ -1,36 +0,0 @@
package com.google.inject
import com.google.inject.binder.LinkedBindingBuilder
import com.google.inject.binder.ScopedBindingBuilder
import kotlin.reflect.KClass
/**
* A wrapper of [LinkedBindingBuilder] that provides convenience function when using
* [KAbstractModule].
*/
@Suppress("DELEGATED_MEMBER_HIDES_SUPERTYPE_OVERRIDE")
open class ExtendedLinkedBindingBuilder<T>
internal constructor(private val delegate: LinkedBindingBuilder<T>) :
LinkedBindingBuilder<T> by delegate {
/**
* An extension function of [LinkedBindingBuilder] that allows users to specify a binding target
* in a Kotlin-idiomatic way.
*
* Usage:
* `bind<...>().toProvider<MyProvider>()`
*
* Usage (with annotation):
* `bind<...>().toProvider<MyProvider>(MyAnnotation::class)`
*
* @param P the type for the binding target's [javax.inject.Provider]
* @sample [KAbstractModuleTest.testExtendedLinkedBindingBuilder_toProvider]
* @sample [KAbstractModuleTest.testExtendedLinkedBindingBuilder_toProviderWithAnnotation]
*
* @return a [ScopedBindingBuilder] for the binding
*/
inline fun <reified P : javax.inject.Provider<out T>> toProvider(
annotation: KClass<out Annotation>? = null
): ScopedBindingBuilder =
toProvider(key<P>(annotation))
}
@@ -1,250 +0,0 @@
// Prevent instantiation from Java
@file:JvmName("-GuiceExtensions")
package com.google.inject
import com.google.inject.binder.AnnotatedBindingBuilder
import com.google.inject.binder.LinkedBindingBuilder
import com.google.inject.binder.ScopedBindingBuilder
import com.google.inject.multibindings.MapBinder
import com.google.inject.multibindings.Multibinder
import kotlin.reflect.KClass
// Functions to create a TypeLiteral and Key
/**
* Returns a new [TypeLiteral] of [T].
*
* Usage: `val myTypeLiteral : TypeLiteral<T> = typeLiteral<String>()`
*
* @param T the type argument to be passed to [TypeLiteral]
* @sample [GuiceExtensionsTest.testTypeLiteral]
*/
inline fun <reified T> typeLiteral(): TypeLiteral<T> = object : TypeLiteral<T>() {}
/**
* Return a new [Key] of [T].
*
* Usage (no annotation): `val myKey : Key<String> = key<String>()`
*
* Usage (with annotation): `val myAnnotatedKey: Key<String> = key<String>(MyAnnotation::class)`
*
* @param T the type argument to be passed to [Key]
* @param annotation the annotation class to annotate the returned Key. When null (the default), the
* [Key] will not be annotated.
* @sample [GuiceExtensionsTest.testKey]
* @sample [GuiceExtensionsTest.testKeyPassingAnnotation]
*/
inline fun <reified T> key(annotation: KClass<out Annotation>? = null): Key<T> =
if (annotation == null) object : Key<T>() {} else object : Key<T>(annotation.java) {}
/**
* Returns a new [Key] of [T].
*
* Usage: `val myKey : Key<String> = key<String>(myAnnotationInstance)`
*
* @param T the type argument to be passed to [Key]
* @param annotation the annotation instance to annotate the returned [Key]
* @sample [GuiceExtensionsTest.testKeyPassingAnnotationInstance]
*/
inline fun <reified T> key(annotation: Annotation): Key<T> = object : Key<T>(annotation) {}
/**
* Returns a new [Key] of [T], using the same annotation (if present) as the given key.
*
* Usage: `val myKey : Key<String> = myOtherKey.ofType<String>()`
*
* @param T the type argument to be passed to [Key]
* @sample [GuiceExtensionsTest.testOfType]
*
* @return a new `Key<T>` with the same annotation (if any) from the source key
*/
inline fun <reified T> Key<*>.ofType(): Key<T> = ofType(typeLiteral<T>())
/**
* Return a new [Key], whose type is the same but whose annotation is the given annotation.
*
* Usage: `val myKey : Key<String> = myKeyOfString.withAnnotation(MyAnnotation::class)`
*
* @param annotation the annotation class used to annotate the returned [Key]
* @sample [GuiceExtensionsTest.testWithAnnotation]
*/
fun <T, A : Annotation> Key<T>.withAnnotation(annotation: KClass<A>): Key<T> =
withAnnotation(annotation.java)
// Extensions for LinkedBindingBuilder.
/**
* An extension function of [LinkedBindingBuilder] that returns a [ScopedBindingBuilder].
*
* WARNING: Make sure to pass a [T] to this function. If you don't, you will end up binding your key
* to itself (see [GuiceExtensionsTest.testBindToMissingTypePointsToItself]).
*
* Usage (no annotation): `bind(...).to<MyImplementation>()`
*
* Usage (with annotation): `bind(...).to<MyImplementation>(MyAnnotation::class)`
*
* @param T the type argument to be passed to the binding target's [Key]
* @param annotation the annotation class used to annotate the binding target's [Key]. When null
* (the default), the target's key will have no annotation.
* @sample [GuiceExtensionsTest.testLinkedBindingBuilderTo]
* @sample [GuiceExtensionsTest.testLinkedBindingBuilderToWithAnnotation]
*/
inline fun <reified T> LinkedBindingBuilder<in T>.to(
annotation: KClass<out Annotation>? = null
): ScopedBindingBuilder = to(key<T>(annotation))
// Note: There is no LinkedBindingBuilder<T>.toProvider() extension function because it would
// require having two type parameters:
// inline fun <reified T, reified S : javax.inject.Provider<T>>
// LinkedBindingBuilder<T>.toProvider(): ScopedBindingBuilder =
// this.toProvider(S::class.java)
// That would cause users to repeat the type (ugly):
// bind<MyType>().toProvider<MyType, MyTypeProvider>()
// Instead, use the inline function ExtendedLinkedBindingBuilder.toProvider or call
// toProvider(key<MyTypeProvider>())
// Extensions for ScopedBindingBuilder
/**
* An extension function of [ScopedBindingBuilder] that allows you to specify the scope of a binding
* without needing to use backticks.
*
* Usage: `bind(...).to(...).inScope<Singleton>()`
*
* @param A the [Scope]'s annotation
* @sample [GuiceExtensionsTest.testInScope]
*/
inline fun <reified A : Annotation> ScopedBindingBuilder.inScope() = this.`in`(A::class.java)
// Extensions for Injector
/**
* Returns an instance of [T] from the [Injector].
*
* Usage (no annotation): `val s : String = injector.getInstance<String>()`
*
* Usage (with annotation): `val s : String = injector.getInstance<String>(MyAnnotation::class)`
*
* @param T the type of the [Key] to get
* @param annotation the annotation class of the [Key] to get. When null (the default), this will
* get an instance of [T] whose [Key] is not annotated.
* @sample [GuiceExtensionsTest.testInjectorGetInstance]
* @sample [GuiceExtensionsTest.testInjectorGetInstanceWithAnnotation]
*
* @return the requested (possibly-annotated) [T]
*/
inline fun <reified T> Injector.getInstance(annotation: KClass<out Annotation>? = null): T =
getInstance(key<T>(annotation))
/**
* Returns a [Provider] of [T] from the [Injector].
*
* Usage (no annotation): `val s : Provider<String> = injector.getProvider<String>() Usage (with
* annotation) `val s : Provider<String> = injector.getProvider<String>(MyAnnotation::class)`
*
* @param T the type of the returned [Provider]'s [Key]
* @param annotation the annotation class of the returned [Provider]'s [Key]. When null (the
* default), this will return a [Provider] whose [Key] is not annotated.
* @sample [GuiceExtensionsTest.testInjectorGetProvider]
* @sample [GuiceExtensionsTest.testInjectorGetProviderWithAnnotation]
*
* @return the requested (possibly-annotated) [Provider] of [T]
*/
inline fun <reified T> Injector.getProvider(
annotation: KClass<out Annotation>? = null
): Provider<T> = getProvider(key<T>(annotation))
// Extensions for AbstractModule
/**
* Returns an [AnnotatedBindingBuilder] for the given type.
*
* Usage: `bind<MyInterface>().to<...>()`
*
* @param T the type to bind
* @sample [AbstractModuleExtensionsTest.testBind]
*/
inline fun <reified T> AbstractModule.bind(): ExtendedAnnotatedBindingBuilder<T> =
`access$ExtendedAnnotatedBindingBuilderConstructor`(`access$binder`().bind(typeLiteral<T>()))
/**
* Returns an [ExtendedLinkedBindingBuilder] for the given type and annotation.
*
* Usage: `bind<MyInterface>(MyAnnotation::class).to<...>()`
*
* @param T the bound [Key]'s type
* @param annotation the bound [Key]'s annotation
* @sample [AbstractModuleExtensionsTest.testBindPassingAnnotation]
*/
inline fun <reified T> AbstractModule.bind(
annotation: KClass<out Annotation>
): ExtendedLinkedBindingBuilder<T> =
`access$ExtendedLinkedBindingBuilderConstructor`(`access$binder`().bind(key<T>(annotation)))
/**
* Returns a [Multibinder] of [T].
*
* Usage (no annotation): `setBinder<String>().addBinding().to(...)`
*
* Usage (with annotation): `setBinder<String>(MyAnnotation::class).addBinding().to(...)`
*
* @param T the type of the [Multibinder]
* @param annotation the [Multibinder]'s annotation (if non-null)
* @sample [AbstractModuleExtensionsTest.testSetBinder]
* @sample [AbstractModuleExtensionsTest.testSetBinderPassingAnnotation]
*/
inline fun <reified T : Any> AbstractModule.setBinder(
annotation: KClass<out Annotation>? = null
): Multibinder<T> = Multibinder.newSetBinder(`access$binder`(), key<T>(annotation))
/**
* Returns a [MapBinder] of [K] and [V].
*
* Usage (no annotation): `mapBinder<String, MyInterface>().addBinding(...).to(...)`
*
* Usage (with annotation): `mapBinder<String,
* MyInterface>(MyAnnotation::class).addBinding(...).to(...)`
*
* @param K the [MapBinder]'s key type
* @param V The [MapBinder]'s value type
* @param annotation the [MapBinder]'s annotation (if non-null)
* @sample [AbstractModuleExtensionsTest.testMapBinder]
* @sample [AbstractModuleExtensionsTest.testMapBinderPassingAnnotation]
*/
inline fun <reified K : Any, reified V : Any> AbstractModule.mapBinder(
annotation: KClass<out Annotation>? = null
): MapBinder<K, V> =
if (annotation == null) {
MapBinder.newMapBinder(`access$binder`(), typeLiteral<K>(), typeLiteral<V>())
} else {
MapBinder.newMapBinder(`access$binder`(), typeLiteral<K>(), typeLiteral<V>(), annotation.java)
}
/**
* Returns a [Provider] of [T].
*
* Usage (no annotation): `getProvider<String>()`
*
* Usage (with annotation): `getProvider<String>(MyAnnotation::class)`
*
* @param T the [Provider]'s type
* @param annotation the annotation for the [Provider]'s [Key]
* @sample [AbstractModuleExtensionsTest.testGetProvider]
*/
inline fun <reified T> AbstractModule.getProvider(
annotation: KClass<out Annotation>? = null
): Provider<T> = `access$binder`().getProvider(key<T>(annotation))
@PublishedApi // needed to access the protected binder() in a protected inline function.
internal fun AbstractModule.`access$binder`(): Binder = binder()
@PublishedApi // needed to access an internal constructor from a protected inline function.
internal fun <T> `access$ExtendedLinkedBindingBuilderConstructor`(
delegate: LinkedBindingBuilder<T>
): ExtendedLinkedBindingBuilder<T> = ExtendedLinkedBindingBuilder(delegate)
@PublishedApi // needed to access an internal constructor from a protected inline function.
internal fun <T> `access$ExtendedAnnotatedBindingBuilderConstructor`(
delegate: AnnotatedBindingBuilder<T>
): ExtendedAnnotatedBindingBuilder<T> = ExtendedAnnotatedBindingBuilder(delegate)
@@ -1,104 +0,0 @@
package com.google.inject
import com.google.inject.internal.Annotations
import com.google.inject.internal.Errors
import com.google.inject.internal.KotlinSupportInterface
import java.lang.reflect.Constructor
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.util.function.Predicate
import kotlin.reflect.KParameter
import kotlin.reflect.KProperty1
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.kotlinFunction
import kotlin.reflect.jvm.kotlinProperty
/**
* Singleton object that contains functions to inspect Kotlin code.
*
* While this is private, it is accessed reflectively by [com.google.inject.internal.KotlinSupport].
*/
private object KotlinSupportImpl : KotlinSupportInterface {
private val NO_ANNOTATIONS: Array<Annotation> = arrayOf()
private val FALSE_PREDICATE = Predicate<Int> { false }
override fun getAnnotations(field: Field): Array<Annotation> {
return if (field.declaringClass.isKotlinClass) {
field.kotlinProperty?.annotations?.toTypedArray() ?: NO_ANNOTATIONS
} else {
NO_ANNOTATIONS
}
}
override fun isNullable(field: Field): Boolean {
return if (field.declaringClass.isKotlinClass) {
field.kotlinProperty?.returnType?.isMarkedNullable ?: false
} else {
false
}
}
override fun getIsParameterKotlinNullablePredicate(constructor: Constructor<*>): Predicate<Int> {
if (!constructor.declaringClass.isKotlinClass) {
return FALSE_PREDICATE
}
val kFunction = constructor.kotlinFunction ?: return FALSE_PREDICATE
return Predicate { index: Int -> kFunction.parameters[index].type.isMarkedNullable }
}
override fun getIsParameterKotlinNullablePredicate(method: Method): Predicate<Int> {
if (!method.declaringClass.isKotlinClass) {
return Predicate<Int> { false }
}
val kFunction = method.kotlinFunction ?: return Predicate<Int> { false }
// Note: Non-static methods have a 'this' parameter at index zero, so the index needs to be
// incremented for that case.
val indexOffset = if (Modifier.isStatic(method.modifiers)) 0 else 1
return Predicate { index: Int ->
val offsettedIndex = index + indexOffset
if (offsettedIndex == kFunction.parameters.size && kFunction.isSuspend) {
// The Continuation object at the end of a suspend function is not visible to the Kotlin
// reflection library so don't invoke `kFunction.parameters[offsettedIndex]` below.
// Instead, just return false.
false
} else {
kFunction.parameters[offsettedIndex].type.isMarkedNullable
}
}
}
override fun checkConstructorParameterAnnotations(constructor: Constructor<*>, errors: Errors) {
if (!constructor.declaringClass.isKotlinClass) return
val parameters = constructor.kotlinFunction?.parameters ?: return
val propertiesByName: Map<String, KProperty1<out Any, *>> =
constructor.declaringClass.kotlin.memberProperties.associateBy { it.name }
for (parameterIndex in parameters.indices) {
val parameter: KParameter = parameters[parameterIndex]
val property: KProperty1<*, *> = propertiesByName[parameter.name] ?: continue
val bindingAnnotation = property.annotations.find {
Annotations.isBindingAnnotation(it.annotationClass.java)
} ?: continue
val bindingAnnotationHasAtTargetMissingParameter =
bindingAnnotation.annotationClass.annotations.filterIsInstance<Target>().any { target ->
!target.allowedTargets.contains(AnnotationTarget.VALUE_PARAMETER)
}
if (bindingAnnotationHasAtTargetMissingParameter) {
errors.atTargetIsMissingParameter(
bindingAnnotation,
parameter.name,
constructor.declaringClass
)
}
}
}
override fun isLocalClass(clazz: Class<*>): Boolean {
return clazz.isKotlinClass && clazz.kotlin.qualifiedName == null
}
private val Class<*>.isKotlinClass: Boolean
get() = this.getDeclaredAnnotation(Metadata::class.java) != null
}
@@ -1,289 +0,0 @@
package com.google.inject
import com.google.common.truth.Truth.assertThat
import com.google.inject.multibindings.MapBinder
import com.google.inject.multibindings.Multibinder
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import javax.inject.Qualifier
@RunWith(JUnit4::class)
class AbstractModuleExtensionsTest {
@Qualifier
annotation class MyAnnotation
interface Foo
class FooImpl : Foo
class FooProvider : Provider<Foo> {
override fun get() = FooImpl()
}
// Below each example is the equivalent code without using the :guice-ktx library.
@Test
@Suppress("UNUSED_VARIABLE", "RemoveExplicitTypeArguments")
fun overview() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
val fooTypeLiteral: TypeLiteral<Foo> = typeLiteral<Foo>()
// val fooTypeLiteral: TypeLiteral<Foo> = object : TypeLiteral<Foo>() {}
val fooKey: Key<Foo> = key<Foo>()
// val fooKey: Key<Foo> = object : Key<Foo>() {}
val annotatedFooKey: Key<Foo> = key<Foo>(MyAnnotation::class)
// val annotatedFooKey: Key<Foo> = object : Key<Foo>(MyAnnotation::class.java)
val stringKey: Key<String> = annotatedFooKey.ofType<String>()
// val stringKey: Key<String> = annotatedFooKey.ofType(String::class.java)
bind<Foo>().to<FooImpl>().inScope<Singleton>()
// bind(Foo::class.java).to(FooImpl::class.java).`in`(Singleton::class.java)
bind<Foo>(MyAnnotation::class).toProvider<FooProvider>()
// bind(object : Key<Foo>(MyAnnotation::class.java){}).toProvider(FooProvider::class.java)
bind<String>().annotatedWith(MyAnnotation::class).toInstance("MyAnnotation")
// bind(String::class.java).annotatedWith(MyAnnotation::class.java).toInstance("MyAnnotation")
val fooProvider: Provider<Foo> = getProvider<Foo>()
// val fooProvider : Provider<Foo> = getProvider(Foo::class.java)
val setBinder: Multibinder<Foo> = setBinder<Foo>()
// val setBinder = Multibinder.newSetBinder(binder(), Foo::class.java)
setBinder.addBinding().to<Foo>()
// setBinder.addBinding().to(object : Key<Foo>() {})
val mapBinder: MapBinder<String, Foo> = mapBinder<String, Foo>()
// val mapBinder = MapBinder.newMapBinder(binder(), String::class.java, Foo::class.java)
mapBinder.addBinding("a").to<Foo>()
// mapBinder.addBinding("a").to(object : Key<Foo>())
}
})
val set: Set<Foo> = injector.getInstance<Set<@JvmSuppressWildcards Foo>>()
// The Guice key for setProvider is actually Key<Set<? extends Foo>> due to Kotlin adding
// wildcards, but fortunately Multibinder provides an alias of Key<Set<? extends Foo>> to
// Key<Set<Foo>>.
val setProvider: Provider<Set<Foo>> = injector.getProvider<Set<Foo>>()
assertThat(set).hasSize(1)
assertThat(set).isEqualTo(setProvider.get())
val map: Map<String, Foo> = injector.getInstance<Map<String, Foo>>()
// The Guice key for mapProvider is actually Key<Map<String, ? extends Foo>> due to Kotlin
// adding wildcards, but fortunately Multibinder provides an alias of
// Key<Map<String, ? extends Foo>> to Key<Map<String, Foo>>.
val mapProvider: Provider<Map<String, Foo>> = injector.getProvider<Map<String, Foo>>()
assertThat(map).hasSize(1)
assertThat(map).isEqualTo(mapProvider.get())
}
@Test
@Suppress("UNUSED_VARIABLE")
fun overviewWithTypeInference() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
// Note: The following does NOT work (i.e. no type inference on anonymous subclassing)
// val fooTypeLiteral: TypeLiteral<Foo> = object : TypeLiteral()
val fooTypeLiteral: TypeLiteral<Foo> = typeLiteral()
val fooKey: Key<Foo> = key()
val annotatedFooKey: Key<Foo> = key(MyAnnotation::class)
val stringKey: Key<String> = annotatedFooKey.ofType()
bind<Foo>().to<FooImpl>().inScope<Singleton>()
bind<Foo>(MyAnnotation::class).toProvider<FooProvider>()
bind<String>().annotatedWith(MyAnnotation::class).toInstance("MyAnnotation")
val fooProvider: Provider<Foo> = getProvider()
val setBinder: Multibinder<Foo> = setBinder()
setBinder.addBinding().to() // adds a binding to Key<Foo>
val mapBinder: MapBinder<String, Foo> = mapBinder()
mapBinder.addBinding("a").to() // adds a binding to Key<Foo>
}
})
val set: Set<Foo> = injector.getInstance()
assertThat(set).hasSize(1)
val map: Map<String, Foo> = injector.getInstance()
assertThat(map).hasSize(1)
}
// Note: The remaining methods test each inline function in isolation
@Test
fun testBind() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind<Foo>().to(FooImpl::class.java)
bind<String?>().toProvider(Provider { null })
}
})
assertThat(injector.getInstance(Foo::class.java)).isInstanceOf(FooImpl::class.java)
assertThat(injector.getInstance(object : Key<String?>() {})).isNull()
}
@Test
fun testBindWithParameterizedType() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind<List<String>>().toInstance(listOf("a", "b"))
}
})
val foo: List<String>? = injector.getInstance(object : Key<List<String>>() {})
assertThat(foo).containsExactly("a", "b")
}
@Test
fun testBindPassingAnnotation() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind<Foo>(MyAnnotation::class).toInstance(FooImpl())
bind<String?>(MyAnnotation::class).toProvider(Provider { null })
}
})
assertThat(injector.getInstance(object : Key<Foo>(MyAnnotation::class.java) {}))
.isInstanceOf(FooImpl::class.java)
assertThat(injector.getInstance(object : Key<String?>(MyAnnotation::class.java) {})).isNull()
}
@Test
fun testExtendedLinkedBindingBuilder_toProvider() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind<Foo>().toProvider<FooProvider>()
}
})
val foo = injector.getInstance(object : Key<Foo>() {})
assertThat(foo).isInstanceOf(FooImpl::class.java)
}
@Test
fun testExtendedLinkedBindingBuilder_toProviderWithAnnotation() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind<FooProvider>(MyAnnotation::class).toInstance(FooProvider())
bind<Foo>().toProvider<FooProvider>(MyAnnotation::class)
}
})
val foo = injector.getInstance(object : Key<Foo>() {})
assertThat(foo).isInstanceOf(FooImpl::class.java)
}
@Test
fun testExtendedAnnotatedBindingBuilder_annotatedWith() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind<Foo>().annotatedWith(MyAnnotation::class).to(FooImpl::class.java)
}
})
val foo = injector.getInstance(object : Key<Foo>(MyAnnotation::class.java) {})
assertThat(foo).isInstanceOf(FooImpl::class.java)
}
@Test
fun testSetBinder() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
val stringBinder: Multibinder<String> = setBinder() // Type-inference magic
stringBinder.addBinding().toInstance("a")
stringBinder.addBinding().toInstance("b")
}
})
val strings: Set<String> =
injector.getInstance(object : Key<Set<@JvmSuppressWildcards String>>() {})
assertThat(strings).containsExactly("a", "b")
}
@Test
fun testSetBinderPassingAnnotation() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
val stringBinder: Multibinder<String> = setBinder<String>(MyAnnotation::class)
stringBinder.addBinding().toInstance("a")
stringBinder.addBinding().toInstance("b")
}
})
val key = object : Key<Set<@JvmSuppressWildcards String>>(MyAnnotation::class.java) {}
val strings: Set<String> = injector.getInstance(key)
assertThat(strings).containsExactly("a", "b")
}
@Test
fun testMapBinder() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
val mapBinder: MapBinder<String, Int> = mapBinder() // Type-inference magic
mapBinder.addBinding("1").toInstance(1)
mapBinder.addBinding("2").toInstance(2)
}
})
val map: Map<String, Int> =
injector.getInstance(object : Key<Map<String, Int>>() {})
assertThat(map).containsExactly("1", 1, "2", 2)
}
@Test
fun testMapBinderPassingAnnotation() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
val mapBinder: MapBinder<String, Int> =
mapBinder(MyAnnotation::class) // Type-inference magic
mapBinder.addBinding("1").toInstance(1)
mapBinder.addBinding("2").toInstance(2)
}
})
val map: Map<String, Int> =
injector.getInstance(object : Key<Map<String, Int>>(MyAnnotation::class.java) {})
assertThat(map).containsExactly("1", 1, "2", 2)
}
@Test
fun testGetProvider() {
val annotatedFooKey: Key<Foo> = object : Key<Foo>(MyAnnotation::class.java) {}
val annotatedStringKey: Key<String?> = object : Key<String?>(MyAnnotation::class.java) {}
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind(Foo::class.java).to(FooImpl::class.java)
bind(annotatedFooKey).toProvider(getProvider<Foo>())
bind(object : Key<String?>() {}).toProvider(Provider { null })
bind(annotatedStringKey).toProvider(getProvider<String?>())
}
})
assertThat(injector.getInstance(annotatedFooKey)).isInstanceOf(FooImpl::class.java)
assertThat(injector.getInstance(annotatedStringKey)).isNull()
}
@Test
fun testAbstractModuleGetProviderPassingAnnotation() {
val annotatedFooKey: Key<Foo> = object : Key<Foo>(MyAnnotation::class.java) {}
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind(annotatedFooKey).to(FooImpl::class.java)
bind(Foo::class.java).toProvider(getProvider<Foo>(MyAnnotation::class))
}
})
val foo: Foo = injector.getInstance(annotatedFooKey)
assertThat(foo).isInstanceOf(FooImpl::class.java)
}
}
@@ -1,183 +0,0 @@
package com.google.inject
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import javax.inject.Qualifier
import kotlin.reflect.full.primaryConstructor
@RunWith(JUnit4::class)
class GuiceExtensionsTest {
@Qualifier
annotation class MyAnnotation
interface Foo
class FooImpl : Foo
@Test
fun testTypeLiteral() {
assertThat(typeLiteral<List<String>>()).isEqualTo(object : TypeLiteral<List<String>>() {})
assertThat(typeLiteral<String?>()).isEqualTo(object : TypeLiteral<String?>() {})
}
@Test
fun testKey() {
assertThat(key<List<String>>()).isEqualTo(object : Key<List<String>>() {})
assertThat(key<String?>()).isEqualTo(object : Key<String?>() {})
}
@Test
fun testKeyPassingAnnotation() {
assertThat(key<List<String>>(MyAnnotation::class))
.isEqualTo(object : Key<List<String>>(MyAnnotation::class.java) {})
assertThat(key<String?>(MyAnnotation::class))
.isEqualTo(object : Key<String?>(MyAnnotation::class.java) {})
}
@Test
fun testKeyPassingAnnotationInstance() {
val annotation: MyAnnotation = MyAnnotation::class.primaryConstructor!!.call()
assertThat(key<List<String>>(annotation))
.isEqualTo(object : Key<List<String>>(annotation) {})
assertThat(key<String?>(annotation))
.isEqualTo(object : Key<String?>(annotation) {})
}
@Test
fun testOfType() {
val key: Key<String> = object : Key<String>(MyAnnotation::class.java) {}
assertThat(key.ofType<Foo>()).isEqualTo(object : Key<Foo>(MyAnnotation::class.java) {})
assertThat(key.ofType<Foo?>()).isEqualTo(object : Key<Foo?>(MyAnnotation::class.java) {})
}
@Test
fun testWithAnnotation() {
assertThat(object : Key<Foo>() {}.withAnnotation(MyAnnotation::class))
.isEqualTo(object : Key<Foo>(MyAnnotation::class.java) {})
assertThat(object : Key<Foo?>() {}.withAnnotation(MyAnnotation::class))
.isEqualTo(object : Key<Foo?>(MyAnnotation::class.java) {})
}
@Test
fun testLinkedBindingBuilderTo() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind(object : Key<Foo?>() {}).to<FooImpl>()
}
})
val foo = injector.getInstance(object : Key<Foo?>() {})
assertThat(foo).isInstanceOf(FooImpl::class.java)
}
@Test
fun testLinkedBindingBuilderToWithAnnotation() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind(object : Key<FooImpl>(MyAnnotation::class.java) {}).toInstance(FooImpl())
bind(object : Key<Foo?>() {}).to<FooImpl>(MyAnnotation::class)
}
})
val foo: Foo? = injector.getInstance(object : Key<Foo?>() {})
assertThat(foo).isInstanceOf(FooImpl::class.java)
}
@Test
fun testBindToMissingTypePointsToItself() {
val module = object : AbstractModule() {
override fun configure() {
// The next line compiles even though the extension method 'to' does not specify a type!
// Kotlin's type-inference mechanism treats the line as if it were:
// bind<Foo>.to<Foo>()
// which binds Foo to itself!
bind(Foo::class.java).to()
}
}
val e: CreationException =
assertThrows(CreationException::class.java) {
Guice.createInjector(module)
}
assertThat(e).hasMessageThat().contains("Binding points to itself")
}
@Test
fun testInScope() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind(Foo::class.java).to(FooImpl::class.java).inScope<Singleton>()
}
})
val foo1: Foo = injector.getInstance(Foo::class.java)
assertThat(foo1).isInstanceOf(FooImpl::class.java)
val foo2 = injector.getInstance(Foo::class.java)
assertThat(foo2).isInstanceOf(FooImpl::class.java)
assertThat(foo1).isSameInstanceAs(foo2)
}
@Test
fun testInjectorGetInstance() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind(object : Key<Foo>() {}).to(FooImpl::class.java)
bind(object : Key<String?>() {}).toProvider(Provider { null })
}
})
val foo1: Foo = injector.getInstance() // Type inference magic
assertThat(foo1).isInstanceOf(FooImpl::class.java)
val foo2 = injector.getInstance<Foo>() // Or specify the type this way
assertThat(foo2).isInstanceOf(FooImpl::class.java)
assertThat(foo1).isNotSameInstanceAs(foo2)
assertThat(injector.getInstance<String?>()).isNull()
}
@Test
fun testInjectorGetInstanceWithAnnotation() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind(object : Key<Foo>(MyAnnotation::class.java) {}).to(FooImpl::class.java)
bind(object : Key<String?>(MyAnnotation::class.java) {}).toProvider(Provider { null })
}
})
val foo1: Foo = injector.getInstance(MyAnnotation::class) // Type inference magic
assertThat(foo1).isInstanceOf(FooImpl::class.java)
val foo2 = injector.getInstance<Foo>(MyAnnotation::class) // Or specify the type this way
assertThat(foo2).isInstanceOf(FooImpl::class.java)
assertThat(foo1).isNotSameInstanceAs(foo2)
assertThat(injector.getInstance<String?>(MyAnnotation::class)).isNull()
}
@Test
fun testInjectorGetProvider() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind(Foo::class.java).to(FooImpl::class.java)
}
})
val foo: Provider<Foo?> = injector.getProvider() // Type inference magic
assertThat(foo.get()).isInstanceOf(FooImpl::class.java)
}
@Test
fun testInjectorGetProviderWithAnnotation() {
val injector =
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind(object : Key<Foo>(MyAnnotation::class.java) {}).to(FooImpl::class.java)
}
})
val foo: Provider<Foo?> = injector.getProvider(MyAnnotation::class) // Type inference magic
assertThat(foo.get()).isInstanceOf(FooImpl::class.java)
}
}
@@ -1,166 +0,0 @@
package com.google.inject
import com.google.common.truth.Truth.assertThat
import com.google.inject.testing.fieldbinder.Bind
import com.google.inject.testing.fieldbinder.BoundFieldModule
import javax.inject.Inject
import kotlin.coroutines.Continuation
import kotlin.test.assertFailsWith
import kotlinx.coroutines.Dispatchers
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class KotlinNullabilityTest {
private class NullablePropertyContainer @Inject constructor(val i: Int, val s: String?)
private class MethodInjectedNullablePropertyContainer {
var i: Int = 0
var s: String? = ""
@Inject
fun inject(i: Int, s: String?) {
this.i = i
this.s = s
}
}
private class SuspendMethodInjectedNullablePropertyContainer {
var i: Int = 0
var s: String? = ""
@Inject
suspend fun inject(i: Int, s: String?) {
this.i = i
this.s = s
}
}
// Note: Despite all attempts to make the companion's inject() static, the Kotlin reflection
// code will still see inject() as a non-static method!
private class NullablePropertyCompanionContainer {
companion object {
var i: Int = 0
var s: String? = ""
@JvmStatic
@Inject
fun inject(i: Int, s: String?) {
this.i = i
this.s = s
}
}
}
private class NullableInjectedFieldContainer {
@Inject var s: String? = ""
}
@Test
fun testNullableBindingIsProvided() {
val value = "value"
val injector = Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind<String>().toInstance(value)
bind<Int>().toInstance(0)
bind<Continuation<Unit>>().toInstance(Continuation(Dispatchers.Default) {})
requestStaticInjection(classForStaticInjection)
}
})
injector.injectMembers(NullablePropertyCompanionContainer.Companion)
assertThat(injector.getInstance<NullablePropertyContainer>().s).isEqualTo(value)
assertThat(injector.getInstance<MethodInjectedNullablePropertyContainer>().s).isEqualTo(value)
assertThat(injector.getInstance<SuspendMethodInjectedNullablePropertyContainer>().s)
.isEqualTo(value)
assertThat(injector.getInstance<NullableInjectedFieldContainer>().s).isEqualTo(value)
assertThat(NullablePropertyCompanionContainer.s).isEqualTo(value)
assertThat(nullableProperty).isEqualTo(value)
}
@Test
fun testNullableBindingIsBoundToNull() {
val injector = Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind<String>().toProvider(Provider { null })
bind<Int>().toInstance(0)
bind<Continuation<Unit>>().toInstance(Continuation(Dispatchers.Default) {})
requestStaticInjection(classForStaticInjection)
}
})
injector.injectMembers(NullablePropertyCompanionContainer.Companion)
assertThat(injector.getInstance<NullablePropertyContainer>().s).isNull()
assertThat(injector.getInstance<MethodInjectedNullablePropertyContainer>().s).isNull()
assertThat(injector.getInstance<SuspendMethodInjectedNullablePropertyContainer>().s).isNull()
assertThat(injector.getInstance<NullableInjectedFieldContainer>().s).isNull()
assertThat(NullablePropertyCompanionContainer.s).isNull()
assertThat(nullableProperty).isNull()
}
class NullablePropertyContainerWithBind(@Bind val s: String? = null)
@Test
fun testNullableBindingWithBoundFieldModule() {
val instance = NullablePropertyContainerWithBind()
val value = Guice.createInjector(BoundFieldModule.of(instance)).getInstance(String::class.java)
assertThat(value).isNull()
}
@Test
fun testNonNullableBindingIsBoundToNull() {
val injector = Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind<String>().toProvider(Provider { null })
bind<Int>().toProvider(Provider { null })
bind<Continuation<Unit>>().toInstance(Continuation(Dispatchers.Default) {})
}
})
assertThrowsNullInjectedIntoNonNullable<ProvisionException> {
injector.getInstance<NullablePropertyContainer>()
}
assertThrowsNullInjectedIntoNonNullable<ProvisionException> {
injector.getInstance<MethodInjectedNullablePropertyContainer>()
}
assertThrowsNullInjectedIntoNonNullable<ProvisionException> {
injector.getInstance<SuspendMethodInjectedNullablePropertyContainer>()
}
assertThrowsNullInjectedIntoNonNullable<ProvisionException> {
injector.injectMembers(NullablePropertyCompanionContainer.Companion)
}
}
@Test
fun testNonNullableIsBoundToNull_staticInjection() {
assertThrowsNullInjectedIntoNonNullable<CreationException> {
Guice.createInjector(object : AbstractModule() {
override fun configure() {
bind<String>().toProvider(Provider { null })
bind<Int>().toProvider(Provider { null })
requestStaticInjection(classForStaticInjection)
}
})
}
}
private inline fun <reified T : Throwable> assertThrowsNullInjectedIntoNonNullable(
block: () -> Unit
) {
val exception = assertFailsWith<T> { block() }
assertThat(exception).hasMessageThat().contains("NULL_INJECTED_INTO_NON_NULLABLE")
assertThat(exception).hasMessageThat().contains("parameter i")
assertThat(exception).hasMessageThat().doesNotContain("parameter s")
}
}
// This holds the java class containing top-level functions.
private val classForStaticInjection: Class<*> = object : Any() {}::class.java.enclosingClass
private var nullableProperty: String? = ""
@Inject
private fun inject(i: Int, s: String?) {
nullableProperty = s
}
@@ -1,41 +0,0 @@
package com.google.inject
import com.google.common.truth.Truth.assertThat
import kotlin.test.assertFailsWith
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
@RunWith(JUnit4::class)
class LocalClassInjectionTest {
@Test
fun testInjectLocalClassWithExternalReference() {
val externalReference = 42
class LocalClass @Inject constructor() {
@Suppress("unused")
private fun existsToReferToExternalReference() = externalReference
}
val ex = assertFailsWith<ConfigurationException> {
Guice.createInjector().getInstance<LocalClass>()
}
assertThat(ex).hasMessageThat().contains(
"Injecting into local classes is not supported. Please use a non-local class instead of " +
"LocalClassInjectionTest\$testInjectLocalClassWithExternalReference\$LocalClass"
)
}
@Test
fun testInjectLocalClassWithNoExternalReference() {
class LocalClass @Inject constructor()
val ex = assertFailsWith<ConfigurationException> {
Guice.createInjector().getInstance<LocalClass>()
}
assertThat(ex).hasMessageThat().contains(
"Injecting into local classes is not supported. Please use a non-local class instead of " +
"LocalClassInjectionTest\$testInjectLocalClassWithNoExternalReference\$LocalClass"
)
}
}