[atomicfu-K/N] Tests for K/N atomicfu-compiler-plugin

* `nativeTest` task now allows to provide compiler plugins that may be enabled during test compilation
* test sets for JVM and K/N backends are equal

KT-60800 describes all the issues with native tests that were solved in this commit.

Co-authored-by: Dmitriy Dolovov <Dmitriy.Dolovov@jetbrains.com>

Merge-request: KT-MR-11401
Merged-by: Maria Sokolova <maria.sokolova@jetbrains.com>
This commit is contained in:
mvicsokolova
2023-08-16 09:41:29 +00:00
committed by Space Team
parent 8f03eb9314
commit 9ff0e0b046
38 changed files with 2644 additions and 33 deletions
@@ -0,0 +1,228 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class AtomicIntArrayInlineExtensionTest {
private val intArr = AtomicIntArray(10)
private val a = atomic(0)
private inline fun casLoop(to: Int): Int {
intArr[0].loop { cur ->
if (intArr[0].compareAndSet(cur, to)) return intArr[0].value
return 777
}
}
private inline fun casLoopExpression(to: Int): Int = intArr[3].loop { cur ->
if (intArr[3].compareAndSet(cur, to)) return intArr[3].value
return 777
}
private inline fun AtomicInt.extensionLoop(to: Int): Int {
loop { cur ->
if (compareAndSet(cur, to)) return value
return 777
}
}
private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur ->
lazySet(cur + 10)
return if (compareAndSet(cur, to)) value else incrementAndGet()
}
private inline fun AtomicInt.extensionLoopMixedReceivers(first: Int, second: Int, index: Int): Int {
loop { cur ->
compareAndSet(cur, first)
intArr[index].compareAndSet(first, second)
return value
}
}
private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int {
loop { cur ->
compareAndSet(cur, to)
a.extensionLoop(5)
return value
}
}
private inline fun AtomicInt.foo(to: Int): Int {
loop { cur ->
if (compareAndSet(cur, to)) return 777
else return value
}
}
private inline fun AtomicInt.bar(delta: Int): Int {
return foo(value + delta)
}
fun test() {
assertEquals(5, casLoop(5))
assertEquals(6, casLoopExpression(6))
assertEquals(66, intArr[1].extensionLoop(66))
assertEquals(66, intArr[2].extensionLoop(66))
assertEquals(77, intArr[1].extensionLoopExpression(777))
assertEquals(99, intArr[1].extensionLoopMixedReceivers(88, 99, 1))
assertEquals(100, intArr[1].extensionLoopRecursive(100))
assertEquals(777, intArr[1].bar(100))
}
}
class AtomicLongArrayInlineExtensionTest {
private val longArr = AtomicLongArray(10)
private val a = atomic(0L)
private inline fun casLoop(to: Long): Long {
longArr[0].loop { cur ->
if (longArr[0].compareAndSet(cur, to)) return longArr[0].value
return 777L
}
}
private inline fun casLoopExpression(to: Long): Long = longArr[3].loop { cur ->
if (longArr[3].compareAndSet(cur, to)) return longArr[3].value
return 777L
}
private inline fun AtomicLong.extensionLoop(to: Long): Long {
loop { cur ->
if (compareAndSet(cur, to)) return value
return 777L
}
}
private inline fun AtomicLong.extensionLoopExpression(to: Long): Long = loop { cur ->
lazySet(cur + 10L)
return if (compareAndSet(cur, to)) value else incrementAndGet()
}
private inline fun AtomicLong.extensionLoopMixedReceivers(first: Long, second: Long, index: Int): Long {
loop { cur ->
compareAndSet(cur, first)
longArr[index].compareAndSet(first, second)
return value
}
}
private inline fun AtomicLong.extensionLoopRecursive(to: Long): Long {
loop { cur ->
compareAndSet(cur, to)
a.extensionLoop(5L)
return value
}
}
private inline fun AtomicLong.foo(to: Long): Long {
loop { cur ->
if (compareAndSet(cur, to)) return 777L
else return value
}
}
private inline fun AtomicLong.bar(delta: Long): Long {
return foo(value + delta)
}
fun test() {
assertEquals(5L, casLoop(5L))
assertEquals(6L, casLoopExpression(6L))
assertEquals(66L, longArr[1].extensionLoop(66L))
assertEquals(66L, longArr[2].extensionLoop(66L))
assertEquals(77L, longArr[1].extensionLoopExpression(777L))
assertEquals(99L, longArr[1].extensionLoopMixedReceivers(88L, 99L, 1))
assertEquals(100L, longArr[1].extensionLoopRecursive(100L))
assertEquals(777L, longArr[1].bar(100L))
}
}
class AtomicBooleanArrayInlineExtensionTest {
private val booleanArr = AtomicBooleanArray(10)
private inline fun casLoop(to: Boolean): Boolean {
booleanArr[0].loop { cur ->
if (booleanArr[0].compareAndSet(cur, to)) return booleanArr[0].value
}
}
private inline fun casLoopExpression(to: Boolean): Boolean = booleanArr[3].loop { cur ->
if (booleanArr[3].compareAndSet(cur, to)) return booleanArr[3].value
}
private inline fun AtomicBoolean.extensionLoop(to: Boolean): Boolean {
loop { cur ->
if (compareAndSet(cur, to)) return value
}
}
private inline fun AtomicBoolean.extensionLoopExpression(to: Boolean): Boolean = loop { cur ->
lazySet(false)
return if (compareAndSet(cur, to)) value else !value
}
private inline fun AtomicBoolean.extensionLoopMixedReceivers(first: Boolean, second: Boolean, index: Int): Boolean {
loop { cur ->
compareAndSet(cur, first)
booleanArr[index].compareAndSet(first, second)
return value
}
}
fun test() {
assertEquals(true, casLoop(true))
assertEquals(true, casLoopExpression(true))
assertEquals(true, booleanArr[1].extensionLoop(true))
assertEquals(true, booleanArr[1].extensionLoopExpression(true))
assertEquals(false, booleanArr[7].extensionLoopMixedReceivers(true, false, 7))
}
}
class AtomicRefArrayInlineExtensionTest {
private val refArr = atomicArrayOfNulls<String?>(10)
private val a = atomic(0L)
private inline fun casLoop(to: String): String? {
refArr[0].loop { cur ->
if (refArr[0].compareAndSet(cur, to)) return refArr[0].value
}
}
private inline fun casLoopExpression(to: String): String? = refArr[3].loop { cur ->
if (refArr[3].compareAndSet(cur, to)) return refArr[3].value
}
private inline fun AtomicRef<String?>.extensionLoop(to: String): String? {
loop { cur ->
if (compareAndSet(cur, to)) return value
else "incorrect"
}
}
private inline fun AtomicRef<String?>.extensionLoopExpression(to: String): String? = loop { cur ->
lazySet("aaa")
return if (compareAndSet(cur, to)) value else "CAS_failed"
}
private inline fun AtomicRef<String?>.extensionLoopMixedReceivers(first: String, second: String, index: Int): String? {
loop { cur ->
compareAndSet(cur, first)
refArr[index].compareAndSet(first, second)
return value
}
}
fun test() {
assertEquals("aaa", casLoop("aaa"))
assertEquals("bbb", casLoopExpression("bbb"))
assertEquals("ccc", refArr[1].extensionLoop("ccc"))
assertEquals("CAS_failed", refArr[1].extensionLoopExpression("ccc"))
assertEquals("bbb", refArr[7].extensionLoopMixedReceivers("aaa", "bbb", 7))
}
}
@Test
fun box() {
AtomicIntArrayInlineExtensionTest().test()
AtomicLongArrayInlineExtensionTest().test()
AtomicBooleanArrayInlineExtensionTest().test()
AtomicRefArrayInlineExtensionTest().test()
}
@@ -0,0 +1,127 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class AtomicIntArrayAtomicfuInlineFunctionsTest {
private val intArr = AtomicIntArray(10)
fun testSetArrayElementValueInLoop() {
intArr[0].loop { cur ->
assertTrue(intArr[0].compareAndSet(cur, 555))
assertEquals(555, intArr[0].value)
return
}
}
private fun action(cur: Int): Int = cur * 1000
fun testArrayElementUpdate() {
intArr[0].lazySet(5)
intArr[0].update { cur -> action(cur) }
assertEquals(intArr[0].value, 5000)
}
fun testArrayElementGetAndUpdate() {
intArr[0].lazySet(5)
assertEquals(intArr[0].getAndUpdate{ cur -> action(cur) }, 5)
assertEquals(intArr[0].value, 5000)
}
fun testArrayElementUpdateAndGet() {
intArr[0].lazySet(5)
assertEquals(intArr[0].updateAndGet{ cur -> action(cur) }, 5000)
}
fun test() {
testSetArrayElementValueInLoop()
testArrayElementUpdate()
testArrayElementGetAndUpdate()
testArrayElementUpdateAndGet()
}
}
class AtomicBooleanArrayAtomicfuInlineFunctionsTest {
private val booleanArr = AtomicBooleanArray(10)
fun testSetArrayElementValueInLoop() {
booleanArr[0].loop { cur ->
assertTrue(booleanArr[0].compareAndSet(cur, true))
assertEquals(true, booleanArr[0].value)
return
}
}
private fun action(cur: Boolean) = !cur
fun testArrayElementUpdate() {
booleanArr[0].lazySet(true)
booleanArr[0].update{ cur -> action(cur) }
assertEquals(booleanArr[0].value, false)
}
fun testArrayElementGetAndUpdate() {
booleanArr[0].lazySet(true)
assertEquals(booleanArr[0].getAndUpdate{ cur -> action(cur) }, true)
assertEquals(booleanArr[0].value, false)
}
fun testArrayElementUpdateAndGet() {
booleanArr[0].lazySet(true)
assertEquals(booleanArr[0].updateAndGet{ cur -> action(cur) }, false)
}
fun test() {
testSetArrayElementValueInLoop()
testArrayElementUpdate()
testArrayElementGetAndUpdate()
testArrayElementUpdateAndGet()
}
}
class AtomicArrayAtomicfuInlineFunctionsTest {
private val anyArr = atomicArrayOfNulls<Any?>(5)
private val refArr = atomicArrayOfNulls<Box>(5)
private data class Box(val n: Int)
fun testSetArrayElementValueInLoop() {
anyArr[0].loop { cur ->
assertTrue(anyArr[0].compareAndSet(cur, IntArray(5)))
return
}
}
private fun action(cur: Box?) = cur?.let { Box(cur.n * 10) }
fun testArrayElementUpdate() {
refArr[0].lazySet(Box(5))
refArr[0].update { cur -> cur?.let { Box(cur.n * 10) } }
assertEquals(refArr[0].value!!.n, 50)
}
fun testArrayElementGetAndUpdate() {
refArr[0].lazySet(Box(5))
assertEquals(refArr[0].getAndUpdate { cur -> action(cur) }!!.n, 5)
assertEquals(refArr[0].value!!.n, 50)
}
fun testArrayElementUpdateAndGet() {
refArr[0].lazySet(Box(5))
assertEquals(refArr[0].updateAndGet { cur -> action(cur) }!!.n, 50)
}
fun test() {
testSetArrayElementValueInLoop()
testArrayElementUpdate()
testArrayElementGetAndUpdate()
testArrayElementUpdateAndGet()
}
}
@Test
fun box() {
AtomicIntArrayAtomicfuInlineFunctionsTest().test()
AtomicBooleanArrayAtomicfuInlineFunctionsTest().test()
AtomicArrayAtomicfuInlineFunctionsTest().test()
}
@@ -0,0 +1,75 @@
import kotlinx.atomicfu.*
import kotlin.test.*
private val topLevelA = atomic(0)
class ComplexLoopTest {
private val a = atomic(10)
private val long = atomic(6757L)
private val b = atomic(11)
private val c = atomic(12)
private val r = atomic<String>("aaa")
private val intArr = AtomicIntArray(10)
private inline fun AtomicInt.fooInt() {
loop { cur ->
if (compareAndSet(cur, 67)) return
}
}
private inline fun AtomicLong.fooLong() {
loop { cur ->
if (compareAndSet(cur, 67L)) return
}
}
private inline fun embeddedLoops(to: Int): Int =
a.loop { aValue ->
if (!a.compareAndSet(aValue, to)) return 666
b.loop { bValue ->
return if (b.compareAndSet(bValue, to)) a.value + b.value else 777
}
}
private inline fun AtomicInt.extensionEmbeddedLoops(to: Int): Int =
loop { cur1 ->
compareAndSet(cur1, to)
loop { cur2 ->
return cur2
}
}
private inline fun embeddedUpdate(to: Int): Int =
a.loop { aValue ->
a.compareAndSet(aValue, to)
return a.updateAndGet { cur -> cur + 100 }
}
private inline fun AtomicRef<String>.extesntionEmbeddedRefUpdate(to: String): String =
loop { value ->
compareAndSet(value, to)
return updateAndGet { cur -> "${cur}AAA" }
}
fun test() {
a.fooInt()
assertEquals(67, a.value)
b.fooInt()
assertEquals(67, b.value)
c.fooInt()
assertEquals(67, c.value)
long.fooLong()
assertEquals(67L, long.value)
assertEquals(24, embeddedLoops(12))
assertEquals(77, c.extensionEmbeddedLoops(77))
assertEquals(66, intArr[0].extensionEmbeddedLoops(66))
assertEquals(166, embeddedUpdate(66))
assertEquals("bbbAAA", r.extesntionEmbeddedRefUpdate("bbb"))
}
}
@Test
fun testComplexLoopTest() {
val testClass = ComplexLoopTest()
testClass.test()
}
@@ -0,0 +1,92 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class ExtensionLoopTest {
private val a = atomic(0)
private val b = atomic(true)
private val l = atomic(5000000000)
private val r = atomic<A>(A("aaaa"))
private val rs = atomic<String>("bbbb")
class A(val s: String)
private inline fun casLoop(to: Int): Int {
a.loop { cur ->
if (a.compareAndSet(cur, to)) return a.value
return 777
}
}
private inline fun casLoopExpression(to: Int): Int = a.loop { cur ->
if (a.compareAndSet(cur, to)) return a.value
return 777
}
private inline fun AtomicInt.extensionLoop(to: Int): Int {
loop { cur ->
if (compareAndSet(cur, to)) return value
return 777
}
}
private inline fun AtomicInt.extensionLoopExpression(to: Int): Int = loop { cur ->
lazySet(cur + 10)
return if (compareAndSet(cur, to)) value + 1 else incrementAndGet()
}
private inline fun AtomicInt.extensionLoopRecursive(to: Int): Int {
loop { cur ->
compareAndSet(cur, to)
extensionLoop(5)
return value
}
}
private inline fun AtomicInt.foo(to: Int): Int {
loop { cur ->
if (compareAndSet(cur, to)) return 777
else return value
}
}
private inline fun AtomicInt.bar(delta: Int): Int {
return foo(value + delta)
}
private inline fun AtomicInt.extensionEmbeddedLoops(to: Int): Int =
loop { cur1 ->
compareAndSet(value, to)
loop { cur2 ->
return cur2
}
}
fun testIntExtensionLoops() {
assertEquals(5, casLoop(5))
assertEquals(45, a.extensionEmbeddedLoops(45))
assertEquals(6, casLoopExpression(6))
assertEquals(17, a.extensionLoopExpression(777))
assertEquals(66, a.extensionLoop(66))
assertEquals(5, a.extensionLoopRecursive(100))
assertEquals(777, a.bar(100))
}
}
private val ref = atomic<String>("aaa")
private inline fun AtomicRef<String>.topLevelExtensionLoop(to: String): String = loop { cur ->
lazySet(cur + to)
return value
}
fun testTopLevelExtensionLoop() {
assertEquals("aaattt", ref.topLevelExtensionLoop("ttt"))
}
@Test
fun testExtensionLoop() {
val testClass = ExtensionLoopTest()
testClass.testIntExtensionLoops()
testTopLevelExtensionLoop()
}
@@ -0,0 +1,114 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class ExtensionsTest {
private val a = atomic(0)
private val l = atomic(0L)
private val s = atomic<String?>(null)
private val b = atomic(true)
fun testScopedFieldGetters() {
check(a.value == 0)
val update = 3
a.lazySet(update)
check(a.compareAndSet(update, 8))
a.lazySet(1)
check(a.value == 1)
check(a.getAndSet(2) == 1)
check(a.value == 2)
check(a.getAndIncrement() == 2)
check(a.value == 3)
check(a.getAndDecrement() == 3)
check(a.value == 2)
check(a.getAndAdd(2) == 2)
check(a.value == 4)
check(a.addAndGet(3) == 7)
check(a.value == 7)
check(a.incrementAndGet() == 8)
check(a.value == 8)
check(a.decrementAndGet() == 7)
check(a.value == 7)
check(a.compareAndSet(7, 10))
}
private inline fun AtomicInt.intExtensionArithmetic() {
value = 0
check(value == 0)
val update = 3
lazySet(update)
check(compareAndSet(update, 8))
lazySet(1)
check(value == 1)
check(getAndSet(2) == 1)
check(value == 2)
check(getAndIncrement() == 2)
check(value == 3)
check(getAndDecrement() == 3)
check(value == 2)
check(getAndAdd(2) == 2)
check(value == 4)
check(addAndGet(3) == 7)
check(value == 7)
check(incrementAndGet() == 8)
check(value == 8)
check(decrementAndGet() == 7)
check(value == 7)
check(compareAndSet(7, 10))
check(compareAndSet(value, 55))
check(value == 55)
}
private inline fun AtomicLong.longExtensionArithmetic() {
value = 2424920024888888848
check(value == 2424920024888888848)
lazySet(8424920024888888848)
check(value == 8424920024888888848)
check(getAndSet(8924920024888888848) == 8424920024888888848)
check(value == 8924920024888888848)
check(incrementAndGet() == 8924920024888888849) // fails
check(value == 8924920024888888849)
check(getAndDecrement() == 8924920024888888849)
check(value == 8924920024888888848)
check(getAndAdd(100000000000000000) == 8924920024888888848)
check(value == 9024920024888888848)
check(addAndGet(-9223372036854775807) == -198452011965886959)
check(value == -198452011965886959)
check(incrementAndGet() == -198452011965886958)
check(value == -198452011965886958)
check(decrementAndGet() == -198452011965886959)
check(value == -198452011965886959)
}
private inline fun AtomicRef<String?>.refExtension() {
value = "aaa"
check(value == "aaa")
lazySet("bb")
check(value == "bb")
check(getAndSet("ccc") == "bb")
check(value == "ccc")
}
private inline fun AtomicBoolean.booleanExtensionArithmetic() {
value = false
check(!value)
lazySet(true)
check(value)
check(getAndSet(true))
check(compareAndSet(value, false))
check(!value)
}
fun testExtension() {
a.intExtensionArithmetic()
l.longExtensionArithmetic()
s.refExtension()
b.booleanExtensionArithmetic()
}
}
@Test
fun box() {
val testClass = ExtensionsTest()
testClass.testScopedFieldGetters()
testClass.testExtension()
}
@@ -0,0 +1,33 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class InlineExtensionWithTypeParameterTest {
abstract class Segment<S : Segment<S>>(val id: Int)
class SemaphoreSegment(id: Int) : Segment<SemaphoreSegment>(id)
private inline fun <S : Segment<S>> AtomicRef<S>.foo(
id: Int,
startFrom: S
): Int {
lazySet(startFrom)
return value.getSegmentId()
}
private inline fun <S : Segment<S>> S.getSegmentId(): Int {
var cur: S = this
return cur.id
}
private val sref = atomic(SemaphoreSegment(0))
fun testInlineExtensionWithTypeParameter() {
val s = SemaphoreSegment(77)
assertEquals(77, sref.foo(0, s))
}
}
@Test
fun box() {
val testClass = InlineExtensionWithTypeParameterTest()
testClass.testInlineExtensionWithTypeParameter()
}
@@ -0,0 +1,52 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LambdaTest {
private val a = atomic(0)
private val rs = atomic<String>("bbbb")
private inline fun <T> inlineLambda(
arg: T,
crossinline block: (T) -> Unit
) = block(arg)
fun loopInLambda1(to: Int) = inlineLambda(to) sc@ { arg ->
a.loop { value ->
a.compareAndSet(value, arg)
return@sc
}
}
fun loopInLambda2(to: Int) = inlineLambda(to) { arg1 ->
inlineLambda(arg1) sc@ { arg2 ->
a.loop { value ->
a.compareAndSet(value, arg2)
return@sc
}
}
}
fun loopInLambda2Ref(to: String) = inlineLambda(to) { arg1 ->
inlineLambda(arg1) sc@ { arg2 ->
rs.loop { value ->
rs.compareAndSet(value, arg2)
return@sc
}
}
}
fun test() {
loopInLambda1(34)
assertEquals(34, a.value)
loopInLambda1(77)
assertEquals(77, a.value)
loopInLambda2Ref("bbb")
assertEquals("bbb", rs.value)
}
}
@Test
fun box() {
val testClass = LambdaTest()
testClass.test()
}
@@ -0,0 +1,57 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LockFreeIntBitsTest {
fun testBasic() {
val bs = LockFreeIntBits()
assertTrue(!bs[0])
assertTrue(bs.bitSet(0))
assertTrue(bs[0])
assertTrue(!bs.bitSet(0))
assertTrue(!bs[1])
assertTrue(bs.bitSet(1))
assertTrue(bs[1])
assertTrue(!bs.bitSet(1))
assertTrue(!bs.bitSet(0))
assertTrue(bs[0])
assertTrue(bs.bitClear(0))
assertTrue(!bs.bitClear(0))
assertTrue(bs[1])
}
}
class LockFreeIntBits {
private val bits = atomic(0)
private fun Int.mask() = 1 shl this
operator fun get(index: Int): Boolean = bits.value and index.mask() != 0
// User-defined private inline function
private inline fun bitUpdate(check: (Int) -> Boolean, upd: (Int) -> Int): Boolean {
bits.update {
if (check(it)) return false
upd(it)
}
return true
}
fun bitSet(index: Int): Boolean {
val mask = index.mask()
return bitUpdate({ it and mask != 0 }, { it or mask })
}
fun bitClear(index: Int): Boolean {
val mask = index.mask()
return bitUpdate({ it and mask == 0 }, { it and mask.inv() })
}
}
@Test
fun box() {
val testClass = LockFreeIntBitsTest()
testClass.testBasic()
}
@@ -0,0 +1,29 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LockTest {
private val inProgressLock = atomic(false)
fun testLock() {
var result = ""
if (inProgressLock.tryAcquire()) {
result = "OK"
}
assertEquals("OK", result)
}
}
// This function will be removed by transformer
@Suppress("NOTHING_TO_INLINE")
private inline fun AtomicBoolean.tryAcquire(): Boolean = compareAndSet(false, true)
// This function is here to test if the Kotlin metadata still consistent after transform
// It is used in ReflectionTest, DO NOT REMOVE
@Suppress("UNUSED_PARAMETER")
fun <AA, BB : Number> String.reflectionTest(mapParam: Map<in AA, BB>): List<BB> = error("no impl")
@Test
fun box() {
val testClass = LockTest()
testClass.testLock()
}
@@ -0,0 +1,28 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class ParameterizedInlineFunExtensionTest {
private inline fun <S> AtomicRef<S>.foo(res1: S, res2: S, foo: (S) -> S): S {
val res = bar(res1, res2)
return res
}
private inline fun <S> AtomicRef<S>.bar(res1: S, res2: S): S {
return res2
}
private val tail = atomic("aaa")
fun testClose() {
assertEquals("bbb", tail.bar("aaa", "bbb"))
val res = tail.foo("bbb", "ccc") { s -> s }
assertEquals("ccc", res)
}
}
@Test
fun testParameterizedInlineFunExtensionTest() {
val testClass = ParameterizedInlineFunExtensionTest()
testClass.testClose()
}