mirror of
https://github.com/google/guice.git
synced 2024-04-21 12:32:36 +00:00
Exclude Kotlin extensions for now since the build setup does not support it yet.
PiperOrigin-RevId: 432524021
This commit is contained in:
@@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user