diff --git a/docs/reference/koin-android/start.md b/docs/reference/koin-android/start.md index 681559ef0..9a9049d7d 100644 --- a/docs/reference/koin-android/start.md +++ b/docs/reference/koin-android/start.md @@ -79,7 +79,7 @@ By using Gradle packge `koin-androidx-startup`, we can use `KoinStartup` interfa ```kotlin class MainApplication : Application(), KoinStartup { - override fun onKoinStartup(): KoinConfiguration = koinConfiguration { + override fun onKoinStartup(): KoinConfiguration = koinConfiguration { androidContext(this@MainApplication) modules(appModule) } @@ -90,7 +90,7 @@ class MainApplication : Application(), KoinStartup { } ``` -This replaces the `startKoin` function that is usally used in `onCreate`. +This replaces the `startKoin` function that is usally used in `onCreate`. The `koinConfiguration` function is returning a `KoinConfiguration` instance. :::info `KoinStartup` avoid blocking main thread at for startup time, and offers better performances. diff --git a/docs/reference/koin-compose/compose.md b/docs/reference/koin-compose/compose.md index 7cf623b62..218e6d581 100644 --- a/docs/reference/koin-compose/compose.md +++ b/docs/reference/koin-compose/compose.md @@ -20,58 +20,59 @@ for an Android/Multiplatform app, use the following packages: - `koin-compose-viewmodel` - Compose ViewModel API - `koin-compose-viewmodel-navigation` - Compose ViewModel API with Navigation API integration -## Starting Koin in a Compose App with KoinApplication +## Starting over an existing Koin context (Koin already started) -The function `KoinApplication` helps to create Koin application instance, as a Composable: +Some time the `startKoin` function is already used in the application, to start Koin in your application (like in Android main app class, the Application class). In that case you need to inform your Compose application about the current Koin context with `KoinContext` or `KoinAndroidContext`. Those functions reuse current Koin context and bind it to the Compose application. ```kotlin @Composable fun App() { - KoinApplication(application = { - modules(...) - }) { - - // your screens here ... + // Set current Koin instance to Compose context + KoinContext() { + MyScreen() } } ``` -The `KoinApplication` function will handle start & stop of your Koin context, regarding the cycle of the Compose context. This function start and stop a new Koin application context. - :::info -In an Android Application, the `KoinApplication` will handle any need to stop/restart Koin context regarding configuration changes or drop of Activities. +Difference between `KoinAndroidContext` and `KoinContext`: +- `KoinAndroidContext` is looking into current Android app context for Koin instance +- `KoinContext` is looking into current GlobalContext for Koin instances ::: :::note -This replaces the use of the classic `startKoin` application function. +If you get some `ClosedScopeException` from a Composable, either use `KoinContext` on your Composable or ensure to have proper Koin start configuration [with Android context](/docs/reference/koin-android/start.md#from-your-application-class) ::: -## Starting over an existing Koin context +## Starting Koin with a Compose App - KoinApplication -Some time the `startKoin` function is already used in the application, to start Koin in your application (like in Android main app class, the Application class). In that case you need to inform your Compose application about the current Koin context with `KoinContext` or `KoinAndroidContext`. Those functions reuse current Koin context and bind it to the Compose application. +The function `KoinApplication` helps to create Koin application instance, as a Composable: ```kotlin @Composable fun App() { - // Set current Koin instance to Compose context - KoinContext() { - + KoinApplication(application = { + modules(...) + }) { + + // your screens here ... MyScreen() } } ``` +The `KoinApplication` function will handle start & stop of your Koin context, regarding the cycle of the Compose context. This function start and stop a new Koin application context. + :::info -Difference between `KoinAndroidContext` and `KoinContext`: -- `KoinAndroidContext` is looking into current Android app context for Koin instance -- `KoinContext` is looking into current GlobalContext for Koin instances +In an Android Application, the `KoinApplication` will handle any need to stop/restart Koin context regarding configuration changes or drop of Activities. ::: :::note -If you get some `ClosedScopeException` from a Composable, either use `KoinContext` on your Composable or ensure to have proper Koin start configuration [with Android context](/docs/reference/koin-android/start.md#from-your-application-class) +This replaces the use of the classic `startKoin` application function. ::: + ### Compose Preview with Koin The `KoinApplication` function is interesting to start dedicated context for preview. This can be also used to help with Compose preview: @@ -122,6 +123,23 @@ fun App(myService: MyService = koinInject()) { } ``` +### Injecting into a @Composable with Parameters + +While you request a new dependency from Koin, you may need to inject parameters. To do this you can use `parameters` parameter of the `koinInject` function, with the `parametersOf()` function like this: + +```kotlin +@Composable +fun App() { + val myService = koinInject(parameters = parametersOf("a_string")) +} +``` + +:::info +You can use parameters with lambda injection like `koinInject{ parametersOf("a_string") }`, but this can have a performance impact if your recomposing a lot around. This version with lambda needs to unwrap your parameters on call, to help avoid remembering your parameters. + +From version 4.0.2 of Koin, koinInject(Qualifier,Scope,ParametersHolder) is introduced to let you use parameters in the most efficient way +::: + ## ViewModel for @Composable The same way you have access to classical single/factory instances, you gain access to the following Koin ViewModel API: diff --git a/docs/setup/koin.md b/docs/setup/koin.md index 089821717..ca46e1966 100644 --- a/docs/setup/koin.md +++ b/docs/setup/koin.md @@ -172,7 +172,7 @@ dependencies { ``` :::info -From now you can continue on Koin Tutorials to learn about using Koin: [Kotlin Multiplatform App Tutorial](/docs/quickstart/kmm) +From now you can continue on Koin Tutorials to learn about using Koin: [Kotlin Multiplatform App Tutorial](/docs/quickstart/kmp) ::: ### **Ktor** diff --git a/examples/androidx-samples/build.gradle b/examples/androidx-samples/build.gradle index 5fd9b4da7..956ce3a70 100644 --- a/examples/androidx-samples/build.gradle +++ b/examples/androidx-samples/build.gradle @@ -68,7 +68,7 @@ dependencies { implementation "io.insert-koin:koin-core-coroutines" implementation "io.insert-koin:koin-androidx-workmanager" implementation "io.insert-koin:koin-androidx-navigation" - implementation "io.insert-koin:koin-androidx-startup" +// implementation "io.insert-koin:koin-androidx-startup" testImplementation "io.insert-koin:koin-test-junit4" testImplementation "io.insert-koin:koin-android-test" } diff --git a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/MainApplication.kt b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/MainApplication.kt index 2583c2826..7e7c88931 100644 --- a/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/MainApplication.kt +++ b/examples/androidx-samples/src/main/java/org/koin/sample/sandbox/MainApplication.kt @@ -11,7 +11,6 @@ import org.koin.android.ext.koin.androidFileProperties import org.koin.android.ext.koin.androidLogger import org.koin.androidx.fragment.koin.fragmentFactory import org.koin.androidx.workmanager.koin.workManagerFactory -import org.koin.androix.startup.KoinStartup import org.koin.core.context.GlobalContext.startKoin import org.koin.core.lazyModules import org.koin.core.logger.Level @@ -22,24 +21,15 @@ import org.koin.mp.KoinPlatform import org.koin.sample.sandbox.di.allModules -class MainApplication : Application(), KoinStartup { +class MainApplication : Application() { - override fun onKoinStartup() = koinConfiguration { - androidLogger(Level.DEBUG) - androidContext(this@MainApplication) - androidFileProperties() - fragmentFactory() - workManagerFactory() -// lazyModules(allModules, dispatcher = IO) - modules(allModules) - } - -// override fun onKoinStartup() : KoinAppDeclaration = { +// override fun onKoinStartup() = koinConfiguration { // androidLogger(Level.DEBUG) // androidContext(this@MainApplication) // androidFileProperties() // fragmentFactory() // workManagerFactory() +//// lazyModules(allModules, dispatcher = IO) // modules(allModules) // } @@ -68,20 +58,20 @@ class MainApplication : Application(), KoinStartup { startTime = System.currentTimeMillis() -// startKoin { -// androidLogger(Level.DEBUG) -// androidContext(this@MainApplication) -// androidFileProperties() -// fragmentFactory() -// workManagerFactory() -//// lazyModules(allModules, dispatcher = IO) -// modules(allModules) -// } + startKoin { + androidLogger(Level.DEBUG) + androidContext(this@MainApplication) + androidFileProperties() + fragmentFactory() + workManagerFactory() +// lazyModules(allModules, dispatcher = IO) + modules(allModules) + } //TODO Load/Unload Koin modules scenario cases cancelPendingWorkManager(this) - KoinPlatform.getKoin().waitAllStartJobs() +// KoinPlatform.getKoin().waitAllStartJobs() } } diff --git a/examples/gradle/versions.gradle b/examples/gradle/versions.gradle index 561a7f967..b1ac36a86 100644 --- a/examples/gradle/versions.gradle +++ b/examples/gradle/versions.gradle @@ -2,7 +2,7 @@ ext { // Kotlin kotlin_version = '2.0.21' // Koin Versions - koin_version = '4.0.1-RC2' + koin_version = '4.0.2-RC3' koin_android_version = koin_version koin_compose_version = koin_version diff --git a/examples/sample-android-compose/src/main/java/org/koin/sample/androidx/compose/App.kt b/examples/sample-android-compose/src/main/java/org/koin/sample/androidx/compose/App.kt index e6f23085e..14a2b7307 100644 --- a/examples/sample-android-compose/src/main/java/org/koin/sample/androidx/compose/App.kt +++ b/examples/sample-android-compose/src/main/java/org/koin/sample/androidx/compose/App.kt @@ -3,8 +3,10 @@ package org.koin.sample.androidx.compose import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.Button import androidx.compose.material.Surface import androidx.compose.material.Text +import androidx.compose.material.TextField import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp @@ -20,6 +22,7 @@ import org.koin.sample.androidx.compose.data.MySingle import org.koin.sample.androidx.compose.di.secondModule import org.koin.sample.androidx.compose.viewmodel.SSHViewModel import org.koin.sample.androidx.compose.viewmodel.UserViewModel +import java.util.UUID @Composable @@ -43,6 +46,9 @@ fun App(userViewModel: UserViewModel = koinViewModel()) { item { ButtonForCreate("-X- Main") { created = !created } } + item { + MyScreen() + } } } else { Surface(modifier = Modifier.padding(8.dp)) { @@ -52,6 +58,27 @@ fun App(userViewModel: UserViewModel = koinViewModel()) { } +@Composable +fun MyScreen() { + + var someValue by remember { mutableStateOf("initial") } + val myDependency = koinInject(parameters = parametersOf(someValue)) + + SideEffect { + println("MyScreen 1") + } + + Column { + Text(text = myDependency.id) + TextField(someValue, onValueChange = { someValue = it }) + Button(onClick = { + if (someValue == "") someValue = "${UUID.randomUUID()}" + }) { + Text("Update") + } + } +} + @Composable fun ViewModelComposable( parentStatus: String = "- status -", @@ -70,21 +97,6 @@ fun ViewModelComposable( } } -// Preview - -//val fakeKoin = module { -// singleOf(::MySingle) -// factoryOf(::MyInnerFactory) -//} -// -//@Preview -//@Composable -//fun PreviewViewModelComposable() { -// KoinApplication(moduleList = { listOf(fakeKoin) }) { -// SingleComposable() -// } -//} - @Composable fun SingleComposable( @@ -105,7 +117,7 @@ fun SingleComposable( @Composable fun FactoryComposable( parentStatus: String = "- status -", - myFactory: MyFactory = koinInject { parametersOf("stable_status") } + myFactory: MyFactory = koinInject(parameters = parametersOf("stable_status")) ) { var created by remember { mutableStateOf(false) } rememberKoinModules(modules = { listOf(secondModule) }) @@ -132,7 +144,7 @@ fun FactoryComposable( //TODO Hold instance until recreate Composable fun InnerFactoryComposable( parentStatus: String, - myFactory: MyInnerFactory = koinInject { parametersOf("_stable_") } + myFactory: MyInnerFactory = koinInject(parameters = parametersOf("_stable_")) ) { var created by remember { mutableStateOf(false) } if (created) { diff --git a/examples/sample-android-compose/src/main/java/org/koin/sample/androidx/compose/MainApplication.kt b/examples/sample-android-compose/src/main/java/org/koin/sample/androidx/compose/MainApplication.kt index dc25fd002..71d5cab6b 100644 --- a/examples/sample-android-compose/src/main/java/org/koin/sample/androidx/compose/MainApplication.kt +++ b/examples/sample-android-compose/src/main/java/org/koin/sample/androidx/compose/MainApplication.kt @@ -14,7 +14,6 @@ class MainApplication : Application() { startKoin { androidLogger(Level.DEBUG) -// printLogger(Level.DEBUG) androidContext(this@MainApplication) modules(appModule) } diff --git a/projects/compose/koin-compose/src/commonMain/kotlin/org/koin/compose/Inject.kt b/projects/compose/koin-compose/src/commonMain/kotlin/org/koin/compose/Inject.kt index a5bf9ff48..537fedc84 100644 --- a/projects/compose/koin-compose/src/commonMain/kotlin/org/koin/compose/Inject.kt +++ b/projects/compose/koin-compose/src/commonMain/kotlin/org/koin/compose/Inject.kt @@ -23,24 +23,72 @@ import org.koin.core.parameter.ParametersHolder import org.koin.core.qualifier.Qualifier import org.koin.core.scope.Scope + /** - * Resolve Koin dependency + * Resolve Koin dependency for given Type T + * + * Note this version unwrap parameters to ParametersHolder in order to let remember all parameters + * This parameters unwrap will be triggered on recomposition * - * @param qualifier - * @param scope - Koin's root default - * @param parameters - injected parameters + * For better performances we advise to use koinInject(Qualifier,Scope,ParametersHolder) + * + * @param qualifier - dependency qualifier + * @param scope - Koin's root by default + * @param parameters - injected parameters (with lambda & parametersOf()) + * @return instance of type T * * @author Arnaud Giuliani */ +@Composable @OptIn(KoinInternalApi::class) +inline fun koinInject( + qualifier: Qualifier? = null, + scope: Scope = currentKoinScope(), + noinline parameters: ParametersDefinition, +): T { + val p = parameters.invoke() + return remember(qualifier, scope, p) { + scope.getWithParameters(T::class, qualifier, p) + } +} + +/** + * Resolve Koin dependency for given Type T + * + * @param qualifier - dependency qualifier + * @param scope - Koin's root by default + * @param parameters - parameters (used with parametersOf(), no lambda) + * @return instance of type T + * + * @author Arnaud Giuliani + */ @Composable +@OptIn(KoinInternalApi::class) inline fun koinInject( qualifier: Qualifier? = null, scope: Scope = currentKoinScope(), - noinline parameters: ParametersDefinition? = null, + parameters: ParametersHolder, +): T { + return remember(qualifier, scope, parameters) { + scope.getWithParameters(T::class, qualifier, parameters) + } +} + +/** + * Resolve Koin dependency for given Type T + * + * @param qualifier - dependency qualifier + * @param scope - Koin's root by default + * @return instance of type T + * + * @author Arnaud Giuliani + */ +@Composable +inline fun koinInject( + qualifier: Qualifier? = null, + scope: Scope = currentKoinScope() ): T { - val params: ParametersHolder? = parameters?.invoke() - return remember(qualifier, scope, params) { - scope.getWithParameters(T::class, qualifier,params) + return remember(qualifier, scope) { + scope.get(T::class, qualifier) } } diff --git a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/error/MissingScopeValueException.kt b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/error/MissingScopeValueException.kt new file mode 100644 index 000000000..85ebafc2a --- /dev/null +++ b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/error/MissingScopeValueException.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2017-Present the original author or 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 org.koin.core.error + +/** + * Scoep value search for given ScopeId is not found + * @author Arnaud Giuliani + */ +class MissingScopeValueException(msg: String) : Exception(msg) diff --git a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/instance/DeclaredScopedInstance.kt b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/instance/DeclaredScopedInstance.kt deleted file mode 100644 index 11782fe4c..000000000 --- a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/instance/DeclaredScopedInstance.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017-Present the original author or 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 org.koin.core.instance - -import org.koin.core.definition.BeanDefinition -import org.koin.core.scope.Scope -import org.koin.core.scope.ScopeID - -/** - * Declared Instance in scope - Value holder to get back the value for the given scope Id - * to avoid ScopedInstanceFactory where we need the bean definition to return a definition - * - * @author Arnaud Giuliani - */ -class DeclaredScopedInstance(beanDefinition: BeanDefinition, val scopeID : ScopeID) : - InstanceFactory(beanDefinition) { - - private var value : T? = null - - fun setValue(v : T){ - value = v - } - - override fun get(context: ResolutionContext): T { - return value ?: error("Scoped instance not found for ${context.scope.id} in $beanDefinition") - } - - override fun isCreated(context: ResolutionContext?): Boolean = value != null - - override fun drop(scope: Scope?) { - value = null - } - - override fun dropAll() { - value = null - } -} \ No newline at end of file diff --git a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/instance/ScopedInstanceFactory.kt b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/instance/ScopedInstanceFactory.kt index 9252c5061..f6fb9a7db 100644 --- a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/instance/ScopedInstanceFactory.kt +++ b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/instance/ScopedInstanceFactory.kt @@ -16,6 +16,7 @@ package org.koin.core.instance import org.koin.core.definition.BeanDefinition +import org.koin.core.error.MissingScopeValueException import org.koin.core.scope.Scope import org.koin.core.scope.ScopeID import org.koin.mp.KoinPlatformTools @@ -24,11 +25,18 @@ import org.koin.mp.KoinPlatformTools * Single instance holder * @author Arnaud Giuliani */ -class ScopedInstanceFactory(beanDefinition: BeanDefinition) : +class ScopedInstanceFactory(beanDefinition: BeanDefinition, val holdInstance : Boolean = true) : InstanceFactory(beanDefinition) { private var values = hashMapOf() + fun size() = values.size + + @PublishedApi + internal fun saveValue(id : ScopeID, value : T){ + values[id] = value + } + override fun isCreated(context: ResolutionContext?): Boolean = (values[context?.scope?.id] != null) override fun drop(scope: Scope?) { @@ -42,20 +50,20 @@ class ScopedInstanceFactory(beanDefinition: BeanDefinition) : return if (values[context.scope.id] == null) { super.create(context) } else { - values[context.scope.id] ?: error("Scoped instance not found for ${context.scope.id} in $beanDefinition") + values[context.scope.id] ?: throw MissingScopeValueException("Factory.create - Scoped instance not found for ${context.scope.id} in $beanDefinition") } } override fun get(context: ResolutionContext): T { if (context.scope.scopeQualifier != beanDefinition.scopeQualifier) { - error("Wrong Scope: trying to open instance for ${context.scope.id} in $beanDefinition") + error("Wrong Scope qualifier: trying to open instance for ${context.scope.id} in $beanDefinition") } KoinPlatformTools.synchronized(this) { - if (!isCreated(context)) { - values[context.scope.id] = create(context) + if (!isCreated(context) && holdInstance) { + values[context.scope.id] = super.create(context) } } - return values[context.scope.id] ?: error("Scoped instance not found for ${context.scope.id} in $beanDefinition") + return values[context.scope.id] ?: throw MissingScopeValueException("Factory.get -Scoped instance not found for ${context.scope.id} in $beanDefinition") } override fun dropAll() { diff --git a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/parameter/ParametersHolder.kt b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/parameter/ParametersHolder.kt index 94cdd49ee..2819afcba 100644 --- a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/parameter/ParametersHolder.kt +++ b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/parameter/ParametersHolder.kt @@ -141,13 +141,14 @@ open class ParametersHolder( override fun toString(): String = "DefinitionParameters${_values.toList()}" override fun equals(other: Any?): Boolean { - if (other is ParametersHolder){ - return this.values == other.values - } else return false + if (this === other) return true + if (other !is ParametersHolder) return false + + return values == other.values && useIndexedValues == other.useIndexedValues } override fun hashCode(): Int { - return values.hashCode() + return 31 * values.hashCode() + (useIndexedValues?.hashCode() ?: 0) } } diff --git a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/InstanceRegistry.kt b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/InstanceRegistry.kt index 11e1ecb16..036f0b0db 100644 --- a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/InstanceRegistry.kt +++ b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/InstanceRegistry.kt @@ -17,11 +17,11 @@ package org.koin.core.registry import org.koin.core.Koin import org.koin.core.annotation.KoinInternalApi +import org.koin.core.definition.BeanDefinition import org.koin.core.definition.IndexKey import org.koin.core.definition.Kind import org.koin.core.definition._createDefinition import org.koin.core.definition.indexKey -import org.koin.core.instance.DeclaredScopedInstance import org.koin.core.instance.ResolutionContext import org.koin.core.instance.InstanceFactory import org.koin.core.instance.NoClass @@ -29,10 +29,12 @@ import org.koin.core.instance.ScopedInstanceFactory import org.koin.core.instance.SingleInstanceFactory import org.koin.core.module.Module import org.koin.core.module.overrideError +import org.koin.core.parameter.ParametersHolder import org.koin.core.qualifier.Qualifier import org.koin.core.scope.Scope import org.koin.core.scope.ScopeID import org.koin.mp.KoinPlatformTools.safeHashMap +import kotlin.collections.toTypedArray import kotlin.reflect.KClass @Suppress("UNCHECKED_CAST") @@ -114,25 +116,28 @@ class InstanceRegistry(val _koin: Koin) { @PublishedApi internal inline fun scopeDeclaredInstance( instance: T, + scopeQualifier: Qualifier, + scopeID: ScopeID, qualifier: Qualifier? = null, secondaryTypes: List> = emptyList(), allowOverride: Boolean = true, - scopeQualifier: Qualifier, - scopeID: ScopeID, + holdInstance : Boolean ) { - val def = _createDefinition(Kind.Scoped, qualifier, { instance }, secondaryTypes, scopeQualifier) - val indexKey = indexKey(def.primaryType, def.qualifier, def.scopeQualifier) - val existingFactory = instances[indexKey] as? DeclaredScopedInstance + val primaryType = T::class + val indexKey = indexKey(primaryType, qualifier, scopeQualifier) + val existingFactory = instances[indexKey] as? ScopedInstanceFactory if (existingFactory != null) { - existingFactory.setValue(instance) + existingFactory.saveValue(scopeID, instance) } else { - val factory = DeclaredScopedInstance(def,scopeID) + val definitionFunction : Scope.(ParametersHolder) -> T = if (!holdInstance) ( { error("Declared definition of type '$primaryType' shouldn't be executed") } ) else ({ instance }) + val def: BeanDefinition = _createDefinition(Kind.Scoped, qualifier, definitionFunction, secondaryTypes, scopeQualifier) + val factory = ScopedInstanceFactory(def, holdInstance = holdInstance) saveMapping(allowOverride, indexKey, factory) def.secondaryTypes.forEach { clazz -> val index = indexKey(clazz, def.qualifier, def.scopeQualifier) saveMapping(allowOverride, index, factory) } - factory.setValue(instance) + factory.saveValue(scopeID, instance) } } @@ -155,14 +160,13 @@ class InstanceRegistry(val _koin: Koin) { } internal fun dropScopeInstances(scope: Scope) { - _instances.values.filterIsInstance>().forEach { factory -> factory.drop(scope) } - _instances.values.removeAll { it is DeclaredScopedInstance<*> && it.scopeID == scope.id } + val factories = _instances.values.toTypedArray() + factories.filterIsInstance>().forEach { factory -> factory.drop(scope) } } internal fun close() { - _instances.forEach { (_, factory) -> - factory.dropAll() - } + val factories = _instances.values.toTypedArray() + factories.forEach { factory -> factory.dropAll() } _instances.clear() } diff --git a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/ScopeRegistry.kt b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/ScopeRegistry.kt index 8952e84a7..5d2c16bed 100644 --- a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/ScopeRegistry.kt +++ b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/registry/ScopeRegistry.kt @@ -90,7 +90,7 @@ class ScopeRegistry(private val _koin: Koin) { } private fun closeAllScopes() { - _scopes.values.toList().forEach { scope -> + _scopes.values.toTypedArray().forEach { scope -> scope.close() } } diff --git a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt index 15b07600b..977c0cd20 100644 --- a/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt +++ b/projects/core/koin-core/src/commonMain/kotlin/org/koin/core/scope/Scope.kt @@ -19,6 +19,7 @@ import org.koin.core.Koin import org.koin.core.annotation.KoinInternalApi import org.koin.core.error.ClosedScopeException import org.koin.core.error.MissingPropertyException +import org.koin.core.error.MissingScopeValueException import org.koin.core.error.NoDefinitionFoundException import org.koin.core.instance.ResolutionContext import org.koin.core.logger.Level @@ -61,7 +62,6 @@ class Scope( @KoinInternalApi private var parameterStack: ThreadLocal>? = null - private var _closed: Boolean = false val logger: Logger get() = _koin.logger @@ -182,6 +182,9 @@ class Scope( } catch (e: NoDefinitionFoundException) { _koin.logger.debug("* No instance found for type '${clazz.getFullName()}' on scope '${toString()}'") null + } catch (e: MissingScopeValueException) { + _koin.logger.debug("* No Scoped value found for type '${clazz.getFullName()}' on scope '${toString()}'") + null } } @@ -394,14 +397,16 @@ class Scope( qualifier: Qualifier? = null, secondaryTypes: List> = emptyList(), allowOverride: Boolean = true, + holdInstance : Boolean = false ) = KoinPlatformTools.synchronized(this) { _koin.instanceRegistry.scopeDeclaredInstance( instance, + scopeQualifier, + id, qualifier, secondaryTypes, allowOverride, - scopeQualifier, - id, + holdInstance = holdInstance ) } @@ -466,10 +471,16 @@ class Scope( */ fun close() = KoinPlatformTools.synchronized(this) { _koin.logger.debug("|- (-) Scope - id:'$id'") + _closed = true + _callbacks.forEach { it.onScopeClose(this) } _callbacks.clear() + sourceValue = null - _closed = true + + parameterStack?.get()?.clear() + parameterStack = null + _koin.scopeRegistry.deleteScope(this) } diff --git a/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/DeclareInstanceJVMTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/DeclareInstanceJVMTest.kt similarity index 58% rename from projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/DeclareInstanceJVMTest.kt rename to projects/core/koin-core/src/commonTest/kotlin/org/koin/core/DeclareInstanceJVMTest.kt index 0ae86b2a1..03ddf006d 100644 --- a/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/DeclareInstanceJVMTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/DeclareInstanceJVMTest.kt @@ -1,9 +1,13 @@ package org.koin.core -import org.junit.Test +import org.koin.core.annotation.KoinInternalApi +import org.koin.core.instance.ScopedInstanceFactory import org.koin.dsl.koinApplication import org.koin.dsl.module -import java.util.* +import org.koin.mp.KoinPlatformTools +import org.koin.mp.generateId +import kotlin.collections.first +import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals @@ -19,15 +23,18 @@ class DeclareInstanceJVMTest { } } + @OptIn(KoinInternalApi::class) @Test fun `should resolve different scoped declared definitions`() { val koin: Koin = koinApplication { modules(module) }.koin + val factory = koin.instanceRegistry.instances.values.first() as ScopedInstanceFactory<*> + // Create two scopes - val scopeA = koin.createScope(UUID.randomUUID().toString(), MyScope()) - val scopeB = koin.createScope(UUID.randomUUID().toString(), MyScope()) + val scopeA = koin.createScope(KoinPlatformTools.generateId(), MyScope()) + val scopeB = koin.createScope(KoinPlatformTools.generateId(), MyScope()) // Declare scope data on each scope val value_A = "A" @@ -35,8 +42,24 @@ class DeclareInstanceJVMTest { val value_B = "B" scopeB.declare(MyScopedData(value_B)) + assertEquals(2, factory.size()) + // Get scope of each data assertNotEquals(scopeA.get().name, scopeB.get().name) + scopeA.close() + scopeB.close() + + assertEquals(0, factory.size()) + + // Create two scopes + val scopeC = koin.createScope(KoinPlatformTools.generateId(), MyScope()) + val value_C = "C" + scopeC.declare(MyScopedData(value_C)) + + assertEquals(1, factory.size()) + + scopeC.close() + assertEquals(0, factory.size()) } @Test diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/DeclareInstanceTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/DeclareInstanceTest.kt index bee031b46..bb517ede2 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/DeclareInstanceTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/DeclareInstanceTest.kt @@ -260,7 +260,7 @@ class DeclareInstanceTest { val session1 = koin.createScope("session1", named("Session")) val session2 = koin.createScope("session2", named("Session")) - session1.declare(a, allowOverride = false) + session1.declare(a, allowOverride = false, holdInstance = true) session2.get() } diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/OverrideAndCreateatStartTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/OverrideAndCreateatStartTest.kt index 9dab6dd1c..4361c0b9e 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/OverrideAndCreateatStartTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/OverrideAndCreateatStartTest.kt @@ -1,5 +1,6 @@ package org.koin.core +import org.koin.KoinCoreTest import org.koin.core.annotation.KoinInternalApi import org.koin.core.context.startKoin import org.koin.core.context.stopKoin @@ -33,7 +34,7 @@ class SomeClassB : SomeClassInterface { } } -class OverrideAndCreateatStartTest { +class OverrideAndCreateatStartTest : KoinCoreTest(){ val moduleA = module { single(createdAtStart = true) { SomeClassA() @@ -46,11 +47,6 @@ class OverrideAndCreateatStartTest { } } - @AfterTest - fun after() { - stopKoin() - } - @OptIn(KoinInternalApi::class) @Test fun testDefinitionOverride() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/ParametersHolderTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/ParametersHolderTest.kt index 06aa53385..9ec04af94 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/ParametersHolderTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/core/ParametersHolderTest.kt @@ -134,4 +134,52 @@ class ParametersHolderTest { assertNotEquals(p1,p3) } + + @Test + fun `equality mutable check`() { + val p1 = parametersOf(1, 2, 3, 4) + val p2 = parametersOf(1, 2, 3, 4) + + assertEquals(p1, p2) + + p2.add(5) + + assertNotEquals(p1, p2) + } + + class DumBParam(v : ArrayList) : ParametersHolder(v) + + @Test + fun `equality check 2`() { + val p1 = DumBParam(arrayListOf(1, 2, 3, 4)) + val p2 = DumBParam(arrayListOf(1, 2, 3, 4)) + val p3 = DumBParam(arrayListOf(1, 2, 3)) + + assertEquals(p1, p2) + + assertNotEquals(p1,p3) + } + + @Test + fun `test equals considers useIndexedValues`() { + val holderWithIndexed = ParametersHolder(mutableListOf(1, 2, 3), useIndexedValues = true) + val holderWithoutIndexed = ParametersHolder(mutableListOf(1, 2, 3), useIndexedValues = false) + + // Assert they are not equal due to differing `useIndexedValues` + assertNotEquals(holderWithIndexed, holderWithoutIndexed, "ParametersHolder instances with different useIndexedValues should not be equal.") + } + + @Test + fun `test mutability affects hashCode and equality`() { + val holder = ParametersHolder(mutableListOf(1, 2, 3)) + val originalHashCode = holder.hashCode() + + // Modify the values list + holder.add(4) + // Assert hashCode changes after modification + assertNotEquals(originalHashCode, holder.hashCode(), "hashCode should reflect changes in the values list.") + // Assert equality is impacted + val holderUnmodified = ParametersHolder(mutableListOf(1, 2, 3)) + assertNotEquals(holder, holderUnmodified, "ParametersHolder should not be equal after its content is modified.") + } } diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/AdditionalTypeBindingTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/AdditionalTypeBindingTest.kt index e1c99db15..a7569331f 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/AdditionalTypeBindingTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/AdditionalTypeBindingTest.kt @@ -1,5 +1,6 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.logger.Level import org.koin.core.qualifier.named @@ -9,7 +10,7 @@ import kotlin.test.Test import kotlin.test.assertEquals // TODO - Check flaky tests -class AdditionalTypeBindingTest { +class AdditionalTypeBindingTest : KoinCoreTest() { // @Test // fun `can resolve an additional type - bind`() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/CloseDefinitionTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/CloseDefinitionTest.kt index e211b862d..d397dbf72 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/CloseDefinitionTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/CloseDefinitionTest.kt @@ -1,5 +1,6 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.context.startKoin import org.koin.core.context.stopKoin @@ -9,7 +10,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class CloseDefinitionTest { +class CloseDefinitionTest : KoinCoreTest() { @Test fun test_onClose() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ConstructorDSLTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ConstructorDSLTest.kt index 988349133..9a0d1e21f 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ConstructorDSLTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ConstructorDSLTest.kt @@ -1,5 +1,6 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.core.logger.Level import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.factoryOf @@ -9,7 +10,7 @@ import kotlin.test.Test import kotlin.test.assertNotNull import kotlin.test.assertNull -class ConstructorDSLTest { +class ConstructorDSLTest : KoinCoreTest() { @Test fun test_reified_type_constructor() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/CreateOnStart.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/CreateOnStart.kt index b7b9cc45b..626fb379a 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/CreateOnStart.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/CreateOnStart.kt @@ -1,5 +1,6 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.component.KoinComponent import org.koin.core.context.startKoin @@ -9,12 +10,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -class CreateOnStart { - - @AfterTest - fun after() { - stopKoin() - } +class CreateOnStart : KoinCoreTest() { @Test fun `works with koin 2_2_3 and koin 3_0_2 breaks with koin 3_1_4`() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/FlattenTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/FlattenTest.kt index ab419d870..95043b948 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/FlattenTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/FlattenTest.kt @@ -1,10 +1,11 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.core.module.flatten import kotlin.test.Test import kotlin.test.assertTrue -class FlattenTest { +class FlattenTest : KoinCoreTest() { @Test fun test_simple_flatten() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/KoinAppCreationTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/KoinAppCreationTest.kt index 5a5a3ea15..cfeb67421 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/KoinAppCreationTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/KoinAppCreationTest.kt @@ -1,5 +1,6 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.Simple.ComponentA import org.koin.Simple.ComponentB import org.koin.core.annotation.KoinInternalApi @@ -19,12 +20,7 @@ import kotlin.test.assertNotNull import kotlin.test.fail @OptIn(KoinInternalApi::class) -class KoinAppCreationTest { - - @AfterTest - fun after() { - stopKoin() - } +class KoinAppCreationTest : KoinCoreTest() { @Test fun `make a Koin application`() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleAndPropertiesTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleAndPropertiesTest.kt index 6149f3948..c3ee79580 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleAndPropertiesTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleAndPropertiesTest.kt @@ -1,12 +1,13 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.error.InstanceCreationException import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.fail -class ModuleAndPropertiesTest { +class ModuleAndPropertiesTest : KoinCoreTest() { @Test fun `get a property from a module`() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleCreationTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleCreationTest.kt index 42ab9caf9..9d8bbe5ea 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleCreationTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleCreationTest.kt @@ -1,12 +1,13 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.logger.Level import org.koin.test.assertDefinitionsCount import kotlin.test.Test import kotlin.test.assertEquals -class ModuleCreationTest { +class ModuleCreationTest : KoinCoreTest() { @Test fun `create an empty module`() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleDeclarationRulesTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleDeclarationRulesTest.kt index f05d74db3..454917042 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleDeclarationRulesTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleDeclarationRulesTest.kt @@ -1,5 +1,6 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.error.DefinitionOverrideException import org.koin.core.logger.Level @@ -8,7 +9,7 @@ import org.koin.test.assertDefinitionsCount import kotlin.test.Test import kotlin.test.assertEquals -class ModuleDeclarationRulesTest { +class ModuleDeclarationRulesTest : KoinCoreTest() { @Test fun `don't allow redeclaration`() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleFactoryIsolationTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleFactoryIsolationTest.kt index 894eabffc..e19bbc08c 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleFactoryIsolationTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleFactoryIsolationTest.kt @@ -1,5 +1,6 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.annotation.KoinInternalApi import org.koin.core.module.Module @@ -14,7 +15,7 @@ val myGetModule : Module get() = module { single { Simple.ComponentA() } } fun myFunModule() = module { single { Simple.ComponentA() } } @OptIn(KoinInternalApi::class) -class ModuleFactoryIsolationTest { +class ModuleFactoryIsolationTest : KoinCoreTest() { @Test fun testVariableIsolationAndInstanceFactories(){ diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleIncludeTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleIncludeTest.kt index cf7bbe0d9..41066bd21 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleIncludeTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleIncludeTest.kt @@ -1,11 +1,12 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.core.module.dsl.singleOf import org.koin.core.qualifier.named import kotlin.test.Test import kotlin.test.assertNotNull -class ModuleIncludeTest { +class ModuleIncludeTest : KoinCoreTest() { @Test fun test_include() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleRestartTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleRestartTest.kt index ad843c035..e3b4cb720 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleRestartTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleRestartTest.kt @@ -1,5 +1,6 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.component.KoinComponent import org.koin.core.component.get @@ -9,7 +10,7 @@ import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test -class ModuleRestartTest : KoinComponent { +class ModuleRestartTest : KoinComponent, KoinCoreTest() { @BeforeTest fun before() { @@ -22,11 +23,6 @@ class ModuleRestartTest : KoinComponent { } } - @AfterTest - fun after() { - stopKoin() - } - @Test fun first_test() { get() diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleSpecialRulesTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleSpecialRulesTest.kt index 253505889..dfd094cf8 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleSpecialRulesTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/ModuleSpecialRulesTest.kt @@ -1,10 +1,11 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.core.qualifier.named import kotlin.test.Test import kotlin.test.assertEquals -class ModuleSpecialRulesTest { +class ModuleSpecialRulesTest : KoinCoreTest() { @Test fun `generic type declaration`() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/NamingTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/NamingTest.kt index 337bea5eb..ddf44ded1 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/NamingTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/NamingTest.kt @@ -1,12 +1,13 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.logger.Level import org.koin.core.qualifier.* import kotlin.test.Test import kotlin.test.assertEquals -class NamingTest { +class NamingTest : KoinCoreTest(){ @Test fun `can resolve naming from root`() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/NewDSLTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/NewDSLTest.kt index 5ef8dce9a..441e7a71b 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/NewDSLTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/NewDSLTest.kt @@ -2,6 +2,7 @@ package org.koin.dsl +import org.koin.KoinCoreTest import org.koin.core.annotation.KoinInternalApi import org.koin.core.instance.FactoryInstanceFactory import org.koin.core.instance.ScopedInstanceFactory @@ -18,7 +19,7 @@ class ClassA2 : IClassA class ClassB(val a: IClassA) interface IClassA -class NewDSLTest { +class NewDSLTest : KoinCoreTest() { @Test fun singleof_options() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/PropertyDefinitionTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/PropertyDefinitionTest.kt index 4764d9f8b..6ff51f9bb 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/PropertyDefinitionTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/dsl/PropertyDefinitionTest.kt @@ -1,10 +1,11 @@ package org.koin.dsl +import org.koin.KoinCoreTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNull -class PropertyDefinitionTest { +class PropertyDefinitionTest : KoinCoreTest(){ @Test fun `load and get properties`() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/full/TODOAppTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/full/TODOAppTest.kt index 393bc7e88..d637e8be1 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/full/TODOAppTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/full/TODOAppTest.kt @@ -1,11 +1,12 @@ package org.koin.full +import org.koin.KoinCoreTest import org.koin.core.logger.Level import org.koin.dsl.koinApplication import org.koin.dsl.module import kotlin.test.* -class TODOAppTest { +class TODOAppTest : KoinCoreTest(){ val defaultModule = module { single { TasksRepository(get()) } diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/AppTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/AppTest.kt index 710c41f42..69a5133b7 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/AppTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/AppTest.kt @@ -1,5 +1,6 @@ package org.koin.koincomponent +import org.koin.KoinCoreTest import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.context.startKoin @@ -16,7 +17,7 @@ class MyApp : KoinComponent { val presenter: TasksPresenter by inject() } -class AppTest { +class AppTest : KoinCoreTest(){ @Test fun can_run_KoinComponent_app() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/CustomKoinComponentTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/CustomKoinComponentTest.kt index b9fe4aeab..ee36f6816 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/CustomKoinComponentTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/CustomKoinComponentTest.kt @@ -1,5 +1,6 @@ package org.koin.koincomponent +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.Koin import org.koin.core.component.KoinComponent @@ -31,7 +32,7 @@ class MyCustomApp : CustomKoinComponent() { val a: Simple.ComponentA by inject() } -class CustomKoinComponentTest { +class CustomKoinComponentTest : KoinCoreTest(){ @Test fun can_inject_KoinComponent_from_custom_instance() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/KoinComponentTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/KoinComponentTest.kt index f6d96f07e..7095c4627 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/KoinComponentTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/KoinComponentTest.kt @@ -1,5 +1,6 @@ package org.koin.koincomponent +import org.koin.KoinCoreTest import org.koin.Simple import org.koin.core.component.KoinComponent import org.koin.core.component.get @@ -20,7 +21,7 @@ class MyLazyComponent : KoinComponent { val anInject: Simple.ComponentA by inject() } -class KoinComponentTest { +class KoinComponentTest : KoinCoreTest(){ @Test fun can_lazy_inject_from_KoinComponent() { diff --git a/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/TODOAppTest.kt b/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/TODOAppTest.kt index 3430000fd..9b070249b 100644 --- a/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/TODOAppTest.kt +++ b/projects/core/koin-core/src/commonTest/kotlin/org/koin/koincomponent/TODOAppTest.kt @@ -1,5 +1,6 @@ package org.koin.koincomponent +import org.koin.KoinCoreTest import org.koin.core.component.KoinComponent import org.koin.core.component.inject import org.koin.core.context.startKoin @@ -10,7 +11,7 @@ import org.koin.dsl.bind import org.koin.dsl.module import kotlin.test.Test -class TODOAppTest { +class TODOAppTest : KoinCoreTest(){ val todoAppModule = module { single { TasksView() } bind TasksContract.View::class diff --git a/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/DebugLogTest.kt b/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/DebugLogTest.kt index 56bceff2d..1bc3265b4 100644 --- a/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/DebugLogTest.kt +++ b/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/DebugLogTest.kt @@ -1,13 +1,14 @@ package org.koin.core import org.junit.Test +import org.koin.KoinCoreTest import org.koin.core.annotation.KoinInternalApi import org.koin.core.context.startKoin import org.koin.core.context.stopKoin import org.koin.core.logger.Level @OptIn(KoinInternalApi::class) -class DebugLogTest { +class DebugLogTest : KoinCoreTest(){ @Test fun fasterDebug() { diff --git a/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/ParametersTest.kt b/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/ParametersTest.kt new file mode 100644 index 000000000..23d0b713d --- /dev/null +++ b/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/ParametersTest.kt @@ -0,0 +1,25 @@ +package org.koin.core + +import org.koin.core.parameter.ParametersHolder +import kotlin.test.Test +import kotlin.test.assertEquals + +class ParametersTest { + + @Test + fun `test thread-safety in concurrent access`() { + val holder = ParametersHolder(mutableListOf(1, 2, 3)) + val threads = mutableListOf() + + for (i in 1..10) { + threads.add(Thread { + holder.add(i) + }) + } + + threads.forEach { it.start() } + threads.forEach { it.join() } + assertEquals(13, holder.size(), "ParametersHolder should contain all elements added concurrently.") + } + +} \ No newline at end of file diff --git a/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/instance/ReflectAPITest.kt b/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/instance/ReflectAPITest.kt index 6f19a8c76..bb448e57d 100644 --- a/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/instance/ReflectAPITest.kt +++ b/projects/core/koin-core/src/jvmTest/kotlin/org/koin/core/instance/ReflectAPITest.kt @@ -1,11 +1,12 @@ package org.koin.core.instance import org.junit.Test +import org.koin.KoinCoreTest import org.koin.core.time.inMs import kotlin.reflect.KClass import kotlin.time.measureTimedValue -class ReflectAPITest { +class ReflectAPITest : KoinCoreTest(){ @Test fun `reflect api to construct an instance`() { diff --git a/projects/core/koin-core/src/jvmTest/kotlin/org/koin/perfs/PerfsTest.kt b/projects/core/koin-core/src/jvmTest/kotlin/org/koin/perfs/PerfsTest.kt index 37bde3854..c3d5f96d4 100644 --- a/projects/core/koin-core/src/jvmTest/kotlin/org/koin/perfs/PerfsTest.kt +++ b/projects/core/koin-core/src/jvmTest/kotlin/org/koin/perfs/PerfsTest.kt @@ -1,5 +1,6 @@ package org.koin.perfs// package org.koin.perfs + import org.koin.KoinCoreTest import kotlin.test.Test import org.koin.core.time.inMs import org.koin.dsl.koinApplication @@ -7,7 +8,7 @@ package org.koin.perfs// package org.koin.perfs import kotlin.time.measureTime import kotlin.time.measureTimedValue - class PerfsTest { + class PerfsTest : KoinCoreTest(){ @Test fun empty_module_perfs() { diff --git a/projects/core/koin-test/src/commonMain/kotlin/org/koin/test/mock/DeclareMock.kt b/projects/core/koin-test/src/commonMain/kotlin/org/koin/test/mock/DeclareMock.kt index 49dd490f0..c701b211a 100644 --- a/projects/core/koin-test/src/commonMain/kotlin/org/koin/test/mock/DeclareMock.kt +++ b/projects/core/koin-test/src/commonMain/kotlin/org/koin/test/mock/DeclareMock.kt @@ -61,7 +61,7 @@ inline fun Scope.declareMock( stubbing: StubFunction = {}, ): T { val mock = MockProvider.makeMock() - declare(mock, qualifier, secondaryTypes + T::class, true) + declare(mock, qualifier, secondaryTypes + T::class, true, holdInstance = true) mock.apply(stubbing) return mock } diff --git a/projects/gradle.properties b/projects/gradle.properties index fdacfea77..f019e6b9a 100644 --- a/projects/gradle.properties +++ b/projects/gradle.properties @@ -1,14 +1,17 @@ #Gradle -org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" +org.gradle.daemon=true +org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M" org.gradle.caching=true org.gradle.configuration-cache=true org.gradle.parallel=true #Kotlin +kotlin.incremental=true +kotlin.incremental.multiplatform=true kotlin.code.style=official #Koin -koinVersion=4.0.1 +koinVersion=4.0.2-RC3 #Compose org.jetbrains.compose.experimental.jscanvas.enabled=true @@ -18,5 +21,5 @@ org.jetbrains.compose.experimental.macos.enabled=true android.useAndroidX=true androidMinSDK=14 androidCompileSDK=34 -#android.nonTransitiveRClass=true +android.nonTransitiveRClass=true