Skip to content

Commit

Permalink
Migrate to atomicfu
Browse files Browse the repository at this point in the history
  • Loading branch information
bitPogo committed Feb 19, 2022
1 parent 9109896 commit 5b51636
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 131 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ object Version {
/**
* [AnitBytes GradlePlugins](https://github.com/bitPogo/gradle-plugins)
*/
const val antibytes = "fa31c9e"
const val antibytes = "5b2583f"

/**
* [Spotless](https://plugins.gradle.org/plugin/com.diffplug.gradle.spotless)
Expand All @@ -30,7 +30,7 @@ object Version {
val antibytes = Antibytes

object Antibytes {
val test = "e453a55-add-ios-SNAPSHOT"
val test = "56c5f3f"
}

val google = Google
Expand Down
1 change: 1 addition & 0 deletions kmock-processor/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ dependencies {

testImplementation(LocalDependency.antibytes.test.core)
testImplementation(LocalDependency.antibytes.test.fixture)
testImplementation(Dependency.multiplatform.stately.collections)
testImplementation(platform(Dependency.jvm.test.junit))
testImplementation(Dependency.jvm.test.kotlin)
testImplementation(Dependency.jvm.test.jupiter)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2022 Matthias Geisler (bitPogo) / All rights reserved.
*
* Use of this source code is governed by Apache v2.0
*/

package tech.antibytes.kmock.fixture

import co.touchlab.stately.isolate.IsolateState
import tech.antibytes.util.test.fixture.PublicApi
import kotlin.random.Random

internal class StringAlphaGenerator(
val random: IsolateState<Random>
) : PublicApi.Generator<String> {
override fun generate(): String {
val length = random.access { it.nextInt(1, 10) }
val chars = ByteArray(length)

for (idx in 0 until length) {
val char = random.access { it.nextInt(65, 91) }
chars[idx] = if (random.access { it.nextBoolean() }) {
(char + 32).toByte()
} else {
char.toByte()
}
}

return chars.decodeToString()
}

companion object : PublicApi.GeneratorFactory<String> {
override fun getInstance(random: IsolateState<Random>): PublicApi.Generator<String> {
return StringAlphaGenerator(random)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,22 @@ import io.mockk.verify
import org.junit.jupiter.api.Test
import tech.antibytes.kmock.MagicStub
import tech.antibytes.kmock.MagicStubCommon
import tech.antibytes.kmock.fixture.StringAlphaGenerator
import tech.antibytes.util.test.fixture.fixture
import tech.antibytes.util.test.fixture.kotlinFixture
import tech.antibytes.util.test.fixture.qualifier.named
import tech.antibytes.util.test.fulfils
import tech.antibytes.util.test.mustBe
import kotlin.test.assertFailsWith

class KMockAggregatorSpec {
private val fixture = kotlinFixture()
private val fixture = kotlinFixture { configuration ->
configuration.addGenerator(
String::class,
StringAlphaGenerator,
named("stringAlpha")
)
}

@Test
fun `It fulfils Aggregator`() {
Expand Down Expand Up @@ -88,7 +96,7 @@ class KMockAggregatorSpec {

every {
annotation.annotationType.resolve().declaration.qualifiedName!!.asString()
} returns if (fixture.random.nextBoolean()) {
} returns if (fixture.random.access { it.nextBoolean() }) {
MagicStub::class.qualifiedName!!
} else {
MagicStubCommon::class.qualifiedName!!
Expand Down Expand Up @@ -127,7 +135,7 @@ class KMockAggregatorSpec {

every {
annotation.annotationType.resolve().declaration.qualifiedName!!.asString()
} returns if (fixture.random.nextBoolean()) {
} returns if (fixture.random.access { it.nextBoolean() }) {
MagicStub::class.qualifiedName!!
} else {
MagicStubCommon::class.qualifiedName!!
Expand Down Expand Up @@ -166,7 +174,7 @@ class KMockAggregatorSpec {
ClassKind.ANNOTATION_CLASS
)

val selector = fixture.random.nextInt(0, selection.lastIndex)
val selector = fixture.random.access { it.nextInt(0, selection.lastIndex) }

val annotation: KSAnnotation = mockk()
val sourceAnnotations: Sequence<KSAnnotation> = sequence {
Expand All @@ -183,12 +191,12 @@ class KMockAggregatorSpec {

val values: List<KSType> = listOf(type)

val className: String = fixture.fixture()
val packageName: String = fixture.fixture()
val className: String = fixture.fixture(named("stringAlpha"))
val packageName: String = fixture.fixture(named("stringAlpha"))

every {
annotation.annotationType.resolve().declaration.qualifiedName!!.asString()
} returns if (fixture.random.nextBoolean()) {
} returns if (fixture.random.access { it.nextBoolean() }) {
MagicStub::class.qualifiedName!!
} else {
MagicStubCommon::class.qualifiedName!!
Expand Down Expand Up @@ -216,7 +224,7 @@ class KMockAggregatorSpec {
KMockAggregator(logger).extractInterfaces(annotated)

// Then
verify(exactly = 1) { logger.error("Cannot stub non interface `$packageName`.`$className`.") }
verify(exactly = 1) { logger.error("Cannot stub non interface $packageName.$className.") }
}

@Test
Expand All @@ -241,12 +249,12 @@ class KMockAggregatorSpec {

val values: List<KSType> = listOf(type)

val className: String = fixture.fixture()
val packageName: String = fixture.fixture()
val className: String = fixture.fixture(named("stringAlpha"))
val packageName: String = fixture.fixture(named("stringAlpha"))

every {
annotation.annotationType.resolve().declaration.qualifiedName!!.asString()
} returns if (fixture.random.nextBoolean()) {
} returns if (fixture.random.access { it.nextBoolean() }) {
MagicStub::class.qualifiedName!!
} else {
MagicStubCommon::class.qualifiedName!!
Expand Down Expand Up @@ -299,12 +307,12 @@ class KMockAggregatorSpec {

val values: List<KSType> = listOf(type)

val className: String = fixture.fixture()
val packageName: String = fixture.fixture()
val className: String = fixture.fixture(named("stringAlpha"))
val packageName: String = fixture.fixture(named("stringAlpha"))

every {
annotation.annotationType.resolve().declaration.qualifiedName!!.asString()
} returns if (fixture.random.nextBoolean()) {
} returns if (fixture.random.access { it.nextBoolean() }) {
MagicStub::class.qualifiedName!!
} else {
MagicStubCommon::class.qualifiedName!!
Expand Down
7 changes: 5 additions & 2 deletions kmock/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ plugins {
id("tech.antibytes.gradle.configuration")
id("tech.antibytes.gradle.publishing")
id("tech.antibytes.gradle.coverage")

id("kotlinx-atomicfu")
}

group = KMockConfiguration.group
Expand Down Expand Up @@ -45,8 +47,8 @@ kotlin {
val commonMain by getting {
dependencies {
implementation(Dependency.multiplatform.kotlin.common)
implementation(Dependency.multiplatform.stately.isolate)
implementation(Dependency.multiplatform.stately.concurrency)
implementation(Dependency.multiplatform.atomicFu.common)
implementation(Dependency.multiplatform.stately.collections)

implementation(LocalDependency.antibytes.test.core)
}
Expand All @@ -56,6 +58,7 @@ kotlin {
implementation(Dependency.multiplatform.test.common)
implementation(Dependency.multiplatform.test.annotations)
implementation(Dependency.multiplatform.coroutines.common)
implementation(Dependency.multiplatform.stately.concurrency)

implementation(LocalDependency.antibytes.test.annotations)
implementation(LocalDependency.antibytes.test.coroutine)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class AsyncFunMockery<ReturnValue, SideEffect : Function<ReturnValue>>(
): ReturnValue {
onEvent(arguments)

return when (provider.get()) {
return when (provider) {
PROVIDER.RETURN_VALUE -> retrieveValue()
PROVIDER.RETURN_VALUES -> retrieveFromValues()
PROVIDER.SIDE_EFFECT -> function()
Expand Down
92 changes: 39 additions & 53 deletions kmock/src/commonMain/kotlin/tech/antibytes/kmock/FunMockery.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

package tech.antibytes.kmock

import co.touchlab.stately.concurrency.AtomicReference
import co.touchlab.stately.concurrency.value
import co.touchlab.stately.isolate.IsolateState
import co.touchlab.stately.collections.IsoMutableList
import co.touchlab.stately.collections.sharedMutableListOf
import kotlinx.atomicfu.AtomicInt
import kotlinx.atomicfu.AtomicRef
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.update
import tech.antibytes.kmock.KMockContract.Collector
import tech.antibytes.util.test.MockError
import kotlin.math.max
Expand All @@ -17,13 +20,14 @@ abstract class FunMockery<ReturnValue, SideEffect : Function<ReturnValue>>(
override val id: String,
collector: Collector = Collector { _, _ -> Unit }
) : KMockContract.FunMockery<ReturnValue, SideEffect> {
private val _returnValue: AtomicReference<ReturnValue?> = AtomicReference(null)
private val _returnValues: AtomicReference<List<ReturnValue>?> = AtomicReference(null)
private val _sideEffect: AtomicReference<SideEffect?> = AtomicReference(null)
private val _calls: AtomicReference<Int> = AtomicReference(0)
protected val provider: AtomicReference<PROVIDER> = AtomicReference(PROVIDER.NO_PROVIDER)
private val arguments: IsolateState<MutableList<Array<out Any?>?>> = IsolateState { mutableListOf() }
private val collector = AtomicReference(collector)
private val _returnValue: AtomicRef<ReturnValue?> = atomic(null)
private val _returnValues: IsoMutableList<ReturnValue> = sharedMutableListOf()
private val _sideEffect: AtomicRef<SideEffect?> = atomic(null)
private val _calls: AtomicInt = atomic(0)
private val _provider: AtomicRef<PROVIDER> = atomic(PROVIDER.NO_PROVIDER)
protected val provider by _provider
private val arguments: IsoMutableList<Array<out Any?>?> = sharedMutableListOf()
private val collector: AtomicRef<Collector> = atomic(collector)

protected enum class PROVIDER(val value: Int) {
NO_PROVIDER(0),
Expand All @@ -35,65 +39,55 @@ abstract class FunMockery<ReturnValue, SideEffect : Function<ReturnValue>>(
private fun setProvider(provider: PROVIDER) {
val activeProvider = max(
provider.value,
this.provider.get().value
this._provider.value.value
)

if (activeProvider == provider.value) {
this.provider.set(provider)
this._provider.update { provider }
}
}

override var returnValue: ReturnValue
@Suppress("UNCHECKED_CAST")
get() = _returnValue.get() as ReturnValue
get() = _returnValue.value as ReturnValue
set(value) {
setProvider(PROVIDER.RETURN_VALUE)
_returnValue.set(value)
_returnValue.update { value }
}

override var returnValues: List<ReturnValue>
get() = _returnValues.get() as List<ReturnValue>
get() = _returnValues
set(values) {
if (values.isEmpty()) {
throw MockError.MissingStub("Empty Lists are not valid as value provider.")
} else {
setProvider(PROVIDER.RETURN_VALUES)
_returnValues.set(values)
_returnValues.clear()
_returnValues.addAll(values)
}
}

override var sideEffect: SideEffect
get() = _sideEffect.get() as SideEffect
get() = _sideEffect.value as SideEffect
set(value) {
setProvider(PROVIDER.SIDE_EFFECT)
_sideEffect.set(value)
_sideEffect.update { value }
}

override val calls: Int
get() = _calls.get()
get() = _calls.value

private fun determineNewValues(currentValues: List<ReturnValue>): List<ReturnValue> {
return if (currentValues.size == 1) {
currentValues
} else {
currentValues.drop(1)
}
}

protected fun retrieveValue(): ReturnValue = _returnValue.get()!!
protected fun retrieveValue(): ReturnValue = _returnValue.value!!

protected fun retrieveFromValues(): ReturnValue {
val currentValues = _returnValues.value
val value = currentValues!!.first()

val newValues = determineNewValues(currentValues)

_returnValues.compareAndSet(currentValues, newValues)

return value
return if (_returnValues.size == 1) {
_returnValues.first()
} else {
_returnValues.removeAt(0)
}
}

protected fun retrieveSideEffect(): SideEffect = _sideEffect.get()!!
protected fun retrieveSideEffect(): SideEffect = _sideEffect.value!!

private fun guardArguments(arguments: Array<out Any?>): Array<out Any?>? {
return if (arguments.isEmpty()) {
Expand All @@ -108,19 +102,11 @@ abstract class FunMockery<ReturnValue, SideEffect : Function<ReturnValue>>(
}

private fun incrementInvocations() {
val calls = this._calls.get()

this._calls.compareAndSet(
calls,
calls + 1
)
this._calls.incrementAndGet()
}

private fun notifyCollector() {
collector.get().addReference(
this,
this._calls.get()
)
collector.value.addReference(this, this._calls.value)
}

protected fun onEvent(arguments: Array<out Any?>) {
Expand All @@ -129,14 +115,14 @@ abstract class FunMockery<ReturnValue, SideEffect : Function<ReturnValue>>(
incrementInvocations()
}

override fun getArgumentsForCall(callIndex: Int): Array<out Any?>? = arguments.access { it[callIndex] }
override fun getArgumentsForCall(callIndex: Int): Array<out Any?>? = arguments[callIndex]

override fun clear() {
_returnValue.set(null)
_returnValues.set(null)
_sideEffect.set(null)
_calls.set(0)
provider.set(PROVIDER.NO_PROVIDER)
_returnValue.update { null }
_returnValues.clear()
_sideEffect.update { null }
_calls.update { 0 }
_provider.update { PROVIDER.NO_PROVIDER }
arguments.access { it.clear() }
}
}
Loading

0 comments on commit 5b51636

Please sign in to comment.