Skip to content

Commit

Permalink
Merge pull request #9 from bitPogo/feature/change-atomics
Browse files Browse the repository at this point in the history
Change to atomicfu
  • Loading branch information
bitPogo authored Feb 19, 2022
2 parents 9109896 + 6a6cfdb commit c0bd181
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 132 deletions.
2 changes: 1 addition & 1 deletion .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ documentation:

# Library
kmock:
- kmock-gradle/**/*
- kmock/**/*

kmock-gradle:
- kmock-gradle/**/*
Expand Down
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 c0bd181

Please sign in to comment.