[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
+49 -13
View File
@@ -4284,32 +4284,68 @@
<sha256 value="25f20bcfb6e13c6b86ec0ec5183c0feb690d8edc4bce3ad991cf2344944c03fb" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu" version="0.21.0">
<artifact name="atomicfu-metadata-0.21.0-all.jar">
<md5 value="2c143a94ae3341744adc6168e136825f" origin="Generated by Gradle"/>
<sha256 value="f31e5b2fd52eb73a0303cffe5c63c8390ca7cf459a02dcf06faa383dd4bb88d3" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu-common" version="0.14.4">
<artifact name="atomicfu-metadata-0.14.4-all.jar">
<md5 value="cf76ac2f212e0cbd2f2b8dc51d69a902" origin="Generated by Gradle"/>
<sha256 value="64b6213c123022daf9da8f070c12fe6f865f89a8c086e2c1f9962239f327d008" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu-js" version="0.17.1">
<artifact name="atomicfu-jsir-0.17.1.klib">
<md5 value="2c628c0646001d82beb86b812a27553e" origin="Generated by Gradle"/>
<sha256 value="398f7e6a21c450160e44f988563ee09dfd40a2b029688d7ed7883ffb28c5d65b" origin="Generated by Gradle"/>
<component group="org.jetbrains.kotlinx" name="atomicfu-js" version="0.21.0">
<artifact name="atomicfu-jsir-0.21.0.klib">
<md5 value="5a499fda383c643df8168b187eeb208a" origin="Generated by Gradle"/>
<sha256 value="f34ac12010dd84d50dd9d04de6cd24c9d5692790cd4231ee4a2ecbc8e7de60cd" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu-jvm" version="0.17.1">
<artifact name="atomicfu-jvm-0.17.1.jar">
<md5 value="7396f6283b01e829a9f290be605b276c" origin="Generated by Gradle"/>
<sha256 value="fcd9bfec8d7dd24b0a350455573720dcd731e972903f982d3fef5dcd2b399946" origin="Generated by Gradle"/>
<component group="org.jetbrains.kotlinx" name="atomicfu-jvm" version="0.21.0">
<artifact name="atomicfu-jvm-0.21.0.jar">
<md5 value="aa61ce985451776e884940095b05bf6a" origin="Generated by Gradle"/>
<sha256 value="1d2c9337b10c48841c217dd0abb3be5a4250c789a8af89a91d707bed6c65b2fb" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu-macosx64" version="0.17.1">
<component group="org.jetbrains.kotlinx" name="atomicfu-linuxx64" version="0.21.0">
<artifact name="atomicfu-cinterop-interop.klib">
<md5 value="64c3271bd490f1e70b1a947265ed421a" origin="Generated by Gradle"/>
<sha256 value="1a2842e7f8f52c5bfb92f669162e1fb51d2cda8e98b3caabe4eccf2eb7ba062b" origin="Generated by Gradle"/>
<md5 value="0c50c02ac0f860747546f8c06820d8a7" origin="Generated by Gradle"/>
<sha256 value="78239c79d67e11a4b8e6205a4ee7d9a946ea6754c768bd7d3a0186838412bc8a" origin="Generated by Gradle"/>
</artifact>
<artifact name="atomicfu.klib">
<md5 value="59090f29cb19ee6daca530c740b23d74" origin="Generated by Gradle"/>
<sha256 value="73117a47d15cca43bbd9edee14db9854f4d35505436b90475aa0467ccff4cf21" origin="Generated by Gradle"/>
<md5 value="608e744e6a8eb997c7cb2652aa1b32b3" origin="Generated by Gradle"/>
<sha256 value="baa808d6967a9beff770aa595c73f9f9487f076213623386a2e81be498a59381" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu-macosarm64" version="0.21.0">
<artifact name="atomicfu-cinterop-interop.klib">
<md5 value="58d70d0e5dbc661e985ef3e9c6ec6d87" origin="Generated by Gradle"/>
<sha256 value="c6f32c6573e1d379ae8a86106808a74e52801d5dfe3e4524e89f79f9946c4677" origin="Generated by Gradle"/>
</artifact>
<artifact name="atomicfu.klib">
<md5 value="37453e7318cd93283ee14a26ffa4ee49" origin="Generated by Gradle"/>
<sha256 value="f9cc52e7fbf91e6cca6d0995e2b4cd23bc25b70a580185e99c0ba2f074bf9dce" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu-macosx64" version="0.21.0">
<artifact name="atomicfu-cinterop-interop.klib">
<md5 value="ca181b6db1fe83c60fcdb84cc389371b" origin="Generated by Gradle"/>
<sha256 value="2e7f2563834e6b42bcbc9fdab2aef29174df26ed256630e112be9f5db6aeb9a5" origin="Generated by Gradle"/>
</artifact>
<artifact name="atomicfu.klib">
<md5 value="91edf3cf2a2aadbc25e64453811667df" origin="Generated by Gradle"/>
<sha256 value="741bcc8db2e255c7d5c06ebaed62636732af428f3115c62ca716f02231f5e321" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="atomicfu-mingwx64" version="0.21.0">
<artifact name="atomicfu-cinterop-interop.klib">
<md5 value="bf0bc36e838273c6789beb997bf748bf" origin="Generated by Gradle"/>
<sha256 value="7ed242a7bc4b3812822102d5d0253704f3ae7d8455ba56b52ffba6dc38eef69b" origin="Generated by Gradle"/>
</artifact>
<artifact name="atomicfu.klib">
<md5 value="9e46a4533329a42c69c8cd694ebe2954" origin="Generated by Gradle"/>
<sha256 value="26e50877925990758b1fc371f5a6c3880325fa241159ded4911c656fcac4480c" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="binary-compatibility-validator" version="0.13.0">
@@ -267,6 +267,16 @@ fun main() {
}
}
// Atomicfu compiler plugin native tests.
testGroup("plugins/atomicfu/atomicfu-compiler/test", "plugins/atomicfu/atomicfu-compiler/testData") {
testClass<AbstractNativeBlackBoxTest>(
suiteTestClassName = "AtomicfuNativeTestGenerated",
annotations = listOf(atomicfuNative(), provider<UseStandardTestCaseGroupProvider>())
) {
model("nativeBox")
}
}
generateTestGroupSuiteWithJUnit5 {
testGroup("native/native.tests/tests-gen", "compiler/util-klib-abi/testData") {
testClass<AbstractNativeLibraryAbiReaderTest>(
@@ -345,4 +355,4 @@ private fun debugger() = annotation(Tag::class.java, "debugger")
private fun infrastructure() = annotation(Tag::class.java, "infrastructure")
private fun k1libContents() = annotation(Tag::class.java, "k1libContents")
private fun k2libContents() = annotation(Tag::class.java, "k2libContents")
private fun atomicfu() = annotation(Tag::class.java, "atomicfu")
private fun atomicfuNative() = annotation(Tag::class.java, "atomicfu-native")
@@ -56,6 +56,7 @@ internal class EnforcedProperties(testClass: Class<*>) {
internal enum class ClassLevelProperty(shortName: String) {
TEST_TARGET("target"),
TEST_MODE("mode"),
COMPILER_PLUGINS("compilerPlugins"),
CUSTOM_KLIBS("customKlibs"),
FORCE_STANDALONE("forceStandalone"),
COMPILE_ONLY("compileOnly"),
@@ -190,6 +190,7 @@ private object NativeTestSupport {
output += sanitizer
output += CacheMode::class to cacheMode
output += computeTestMode(enforcedProperties)
output += computeCompilerPlugins(enforcedProperties)
output += computeCustomKlibs(enforcedProperties)
output += computeForcedStandaloneTestKind(enforcedProperties)
output += computeForcedNoopTestRunner(enforcedProperties)
@@ -277,6 +278,15 @@ private object NativeTestSupport {
private fun computeTestMode(enforcedProperties: EnforcedProperties): TestMode =
ClassLevelProperty.TEST_MODE.readValue(enforcedProperties, TestMode.values(), default = TestMode.TWO_STAGE_MULTI_MODULE)
private fun computeCompilerPlugins(enforcedProperties: EnforcedProperties): CompilerPlugins =
CompilerPlugins(
ClassLevelProperty.COMPILER_PLUGINS.readValue(
enforcedProperties,
{ it.split(':', ';').mapToSet(::File) },
default = emptySet()
)
)
private fun computeCustomKlibs(enforcedProperties: EnforcedProperties): CustomKlibs =
CustomKlibs(
ClassLevelProperty.CUSTOM_KLIBS.readValue(
@@ -35,6 +35,7 @@ internal abstract class BasicCompilation<A : TestCompilationArtifact>(
private val optimizationMode: OptimizationMode,
private val compilerOutputInterceptor: CompilerOutputInterceptor,
protected val freeCompilerArgs: TestCompilerArgs,
protected val compilerPlugins: CompilerPlugins,
protected val dependencies: CategorizedDependencies,
protected val expectedArtifact: A
) : TestCompilation<A>() {
@@ -67,6 +68,10 @@ internal abstract class BasicCompilation<A : TestCompilationArtifact>(
add(freeCompilerArgs.compilerArgs)
}
private fun ArgsBuilder.applyCompilerPlugins() {
add(compilerPlugins.compilerPluginJars) { compilerPluginJar -> "-Xplugin=${compilerPluginJar.path}" }
}
private fun ArgsBuilder.applySources() {
addFlattenedTwice(sourceModules, { it.files }) { it.location.path }
}
@@ -79,6 +84,7 @@ internal abstract class BasicCompilation<A : TestCompilationArtifact>(
applySpecificArgs(this)
applyDependencies(this)
applyFreeArgs()
applyCompilerPlugins()
applySources()
}
@@ -135,6 +141,7 @@ internal abstract class SourceBasedCompilation<A : TestCompilationArtifact>(
private val allocator: Allocator,
private val pipelineType: PipelineType,
freeCompilerArgs: TestCompilerArgs,
compilerPlugins: CompilerPlugins,
override val sourceModules: Collection<TestModule>,
dependencies: CategorizedDependencies,
expectedArtifact: A
@@ -145,6 +152,7 @@ internal abstract class SourceBasedCompilation<A : TestCompilationArtifact>(
optimizationMode = optimizationMode,
compilerOutputInterceptor = compilerOutputInterceptor,
freeCompilerArgs = freeCompilerArgs,
compilerPlugins = compilerPlugins,
dependencies = dependencies,
expectedArtifact = expectedArtifact
) {
@@ -197,6 +205,7 @@ internal class LibraryCompilation(
allocator = settings.get(),
pipelineType = settings.get(),
freeCompilerArgs = freeCompilerArgs,
compilerPlugins = settings.get(),
sourceModules = sourceModules,
dependencies = CategorizedDependencies(dependencies),
expectedArtifact = expectedArtifact
@@ -231,6 +240,7 @@ internal class ObjCFrameworkCompilation(
allocator = settings.get(),
pipelineType = settings.getStageDependentPipelineType(),
freeCompilerArgs = freeCompilerArgs,
compilerPlugins = settings.get(),
sourceModules = sourceModules,
dependencies = CategorizedDependencies(dependencies),
expectedArtifact = expectedArtifact
@@ -317,6 +327,7 @@ internal class ExecutableCompilation(
allocator = settings.get(),
pipelineType = settings.getStageDependentPipelineType(),
freeCompilerArgs = freeCompilerArgs,
compilerPlugins = settings.get(),
sourceModules = sourceModules,
dependencies = CategorizedDependencies(dependencies),
expectedArtifact = expectedArtifact
@@ -409,6 +420,7 @@ internal class StaticCacheCompilation(
optimizationMode = settings.get(),
compilerOutputInterceptor = settings.get(),
freeCompilerArgs = freeCompilerArgs,
compilerPlugins = settings.get(),
dependencies = CategorizedDependencies(dependencies),
expectedArtifact = expectedArtifact
) {
@@ -77,6 +77,19 @@ internal enum class TestMode(private val description: String) {
override fun toString() = description
}
/**
* Kotlin compiler plugins to be used together with the the Kotlin/Native compiler.
*/
@JvmInline
internal value class CompilerPlugins(val compilerPluginJars: Set<File>) {
init {
val invalidJars = compilerPluginJars.filterNot { it.isDirectory || (it.isFile && it.extension == "jar") }
assertTrue(invalidJars.isEmpty()) {
"There are invalid compiler plugin JARs that should be passed for the Kotlin/Native compiler: ${invalidJars.joinToString { "[$it]" }}"
}
}
}
/**
* The set of custom (external) klibs that should be passed to the Kotlin/Native compiler.
*/
@@ -41,8 +41,6 @@ val atomicfuNativeKlib by configurations.creating {
attribute(KotlinNativeTarget.konanTargetAttribute, nativeTargetName)
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API))
attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
// todo: don't add platform specific attribute
attribute(KotlinNativeTarget.konanTargetAttribute, org.jetbrains.kotlin.konan.target.KonanTarget.MACOS_X64.toString())
}
}
@@ -54,6 +52,8 @@ val atomicfuJsIrRuntimeForTests by configurations.creating {
}
}
val atomicfuCompilerPluginForTests by configurations.creating
repositories {
mavenCentral()
}
@@ -105,7 +105,7 @@ dependencies {
testImplementation(projectTests(":compiler:tests-common"))
testImplementation(projectTests(":compiler:tests-common-new"))
testImplementation(projectTests(":compiler:test-infrastructure"))
testCompileOnly("org.jetbrains.kotlinx:atomicfu:0.17.1") // todo: do not hardcode atomicfu version
testCompileOnly("org.jetbrains.kotlinx:atomicfu:0.21.0")
testApiJUnit5()
@@ -114,10 +114,32 @@ dependencies {
testRuntimeOnly(project(":compiler:backend-common"))
testRuntimeOnly(commonDependency("org.fusesource.jansi", "jansi"))
atomicfuJsClasspath("org.jetbrains.kotlinx:atomicfu-js:0.17.1") { isTransitive = false }
atomicfuJsClasspath("org.jetbrains.kotlinx:atomicfu-js:0.21.0") { isTransitive = false }
atomicfuJsIrRuntimeForTests(project(":kotlinx-atomicfu-runtime")) { isTransitive = false }
atomicfuJvmClasspath("org.jetbrains.kotlinx:atomicfu:0.17.1") { isTransitive = false }
atomicfuNativeKlib("org.jetbrains.kotlinx:atomicfu:0.17.1") { isTransitive = false }
atomicfuJvmClasspath("org.jetbrains.kotlinx:atomicfu:0.21.0") { isTransitive = false }
atomicfuNativeKlib("org.jetbrains.kotlinx:atomicfu:0.21.0") { isTransitive = false }
atomicfuCompilerPluginForTests(project(":kotlin-atomicfu-compiler-plugin"))
// Implicit dependencies on native artifacts to run native tests on CI
implicitDependencies("org.jetbrains.kotlinx:atomicfu-linuxx64:0.21.0") {
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API))
}
}
implicitDependencies("org.jetbrains.kotlinx:atomicfu-macosarm64:0.21.0"){
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API))
}
}
implicitDependencies("org.jetbrains.kotlinx:atomicfu-macosx64:0.21.0"){
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API))
}
}
implicitDependencies("org.jetbrains.kotlinx:atomicfu-mingwx64:0.21.0"){
attributes {
attribute(Usage.USAGE_ATTRIBUTE, objects.named(KotlinUsages.KOTLIN_API))
}
}
embedded(project(":kotlinx-atomicfu-runtime")) {
attributes {
@@ -128,7 +150,7 @@ dependencies {
isTransitive = false
}
testImplementation("org.jetbrains.kotlinx:atomicfu:0.17.1")
testImplementation("org.jetbrains.kotlinx:atomicfu:0.21.0")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.9.1")
}
@@ -146,7 +168,10 @@ testsJar()
useD8Plugin()
projectTest(jUnitMode = JUnitMode.JUnit5) {
useJUnitPlatform()
useJUnitPlatform {
// Exclude all tests with the "atomicfu-native" tag. They should be launched by another test task.
excludeTags("atomicfu-native")
}
useJsIrBoxTests(version = version, buildDir = "$buildDir/")
workingDir = rootDir
@@ -154,6 +179,12 @@ projectTest(jUnitMode = JUnitMode.JUnit5) {
dependsOn(":dist")
dependsOn(atomicfuJsIrRuntimeForTests)
// Depend on the test task that launches Native tests so that it will also run together with tests
// for all other targets if K/N is enabled
if (kotlinBuildProperties.isKotlinNativeEnabled) {
dependsOn(nativeTest)
}
val localAtomicfuJsIrRuntimeForTests: FileCollection = atomicfuJsIrRuntimeForTests
val localAtomicfuJsClasspath: FileCollection = atomicfuJsClasspath
val localAtomicfuJvmClasspath: FileCollection = atomicfuJvmClasspath
@@ -168,10 +199,11 @@ projectTest(jUnitMode = JUnitMode.JUnit5) {
publish()
standardPublicJars()
val nativeBoxTest = nativeTest(
taskName = "nativeBoxTest",
tag = "atomicfu",
val nativeTest = nativeTest(
taskName = "nativeTest",
tag = "atomicfu-native", // Include all tests with the "atomicfu-native" tag.
requirePlatformLibs = true,
customDependencies = listOf(atomicfuJvmClasspath),
customKlibDependencies = listOf(atomicfuNativeKlib)
customCompilerDependencies = listOf(atomicfuJvmClasspath),
customTestDependencies = listOf(atomicfuNativeKlib),
compilerPluginDependencies = listOf(atomicfuCompilerPluginForTests)
)
@@ -0,0 +1,276 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.konan.blackboxtest;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.junit.jupiter.api.Tag;
import org.jetbrains.kotlin.konan.blackboxtest.support.group.UseStandardTestCaseGroupProvider;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.GenerateNativeTestsKt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/nativeBox")
@TestDataPath("$PROJECT_ROOT")
@Tag("atomicfu-native")
@UseStandardTestCaseGroupProvider()
public class AtomicfuNativeTestGenerated extends AbstractNativeBlackBoxTest {
@Test
public void testAllFilesPresentInNativeBox() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/nativeBox"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Nested
@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions")
@TestDataPath("$PROJECT_ROOT")
@Tag("atomicfu-native")
@UseStandardTestCaseGroupProvider()
public class Atomic_extensions {
@Test
public void testAllFilesPresentInAtomic_extensions() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("ArrayInlineExtensionTest.kt")
public void testArrayInlineExtensionTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions/ArrayInlineExtensionTest.kt");
}
@Test
@TestMetadata("ArrayLoopTest.kt")
public void testArrayLoopTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions/ArrayLoopTest.kt");
}
@Test
@TestMetadata("ComplexLoopTest.kt")
public void testComplexLoopTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions/ComplexLoopTest.kt");
}
@Test
@TestMetadata("ExtensionLoopTest.kt")
public void testExtensionLoopTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions/ExtensionLoopTest.kt");
}
@Test
@TestMetadata("ExtensionsTest.kt")
public void testExtensionsTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions/ExtensionsTest.kt");
}
@Test
@TestMetadata("InlineExtensionWithTypeParameterTest.kt")
public void testInlineExtensionWithTypeParameterTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions/InlineExtensionWithTypeParameterTest.kt");
}
@Test
@TestMetadata("LambdaTest.kt")
public void testLambdaTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions/LambdaTest.kt");
}
@Test
@TestMetadata("LockFreeIntBitsTest.kt")
public void testLockFreeIntBitsTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions/LockFreeIntBitsTest.kt");
}
@Test
@TestMetadata("LockTest.kt")
public void testLockTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions/LockTest.kt");
}
@Test
@TestMetadata("ParameterizedInlineFunExtensionTest.kt")
public void testParameterizedInlineFunExtensionTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomic_extensions/ParameterizedInlineFunExtensionTest.kt");
}
}
@Nested
@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic")
@TestDataPath("$PROJECT_ROOT")
@Tag("atomicfu-native")
@UseStandardTestCaseGroupProvider()
public class Atomics_basic {
@Test
public void testAllFilesPresentInAtomics_basic() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("ArithmeticTest.kt")
public void testArithmeticTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/ArithmeticTest.kt");
}
@Test
@TestMetadata("AtomicArrayTest.kt")
public void testAtomicArrayTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/AtomicArrayTest.kt");
}
@Test
@TestMetadata("IndexArrayElementGetterTest.kt")
public void testIndexArrayElementGetterTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/IndexArrayElementGetterTest.kt");
}
@Test
@TestMetadata("InitializationOrderTest.kt")
public void testInitializationOrderTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/InitializationOrderTest.kt");
}
@Test
@TestMetadata("LateinitPropertiesTest.kt")
public void testLateinitPropertiesTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/LateinitPropertiesTest.kt");
}
@Test
@TestMetadata("LockFreeLongCounterTest.kt")
public void testLockFreeLongCounterTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/LockFreeLongCounterTest.kt");
}
@Test
@TestMetadata("LockFreeQueueTest.kt")
public void testLockFreeQueueTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/LockFreeQueueTest.kt");
}
@Test
@TestMetadata("LockFreeStackTest.kt")
public void testLockFreeStackTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/LockFreeStackTest.kt");
}
@Test
@TestMetadata("LoopTest.kt")
public void testLoopTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/LoopTest.kt");
}
@Test
@TestMetadata("MultiInitTest.kt")
public void testMultiInitTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/MultiInitTest.kt");
}
@Test
@TestMetadata("ScopeTest.kt")
public void testScopeTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/ScopeTest.kt");
}
@Test
@TestMetadata("SimpleLockTest.kt")
public void testSimpleLockTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/SimpleLockTest.kt");
}
@Test
@TestMetadata("UncheckedCastTest.kt")
public void testUncheckedCastTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/atomics_basic/UncheckedCastTest.kt");
}
}
@Nested
@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/delegated")
@TestDataPath("$PROJECT_ROOT")
@Tag("atomicfu-native")
@UseStandardTestCaseGroupProvider()
public class Delegated {
@Test
public void testAllFilesPresentInDelegated() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/delegated"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("DelegatedPropertiesTest.kt")
public void testDelegatedPropertiesTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/delegated/DelegatedPropertiesTest.kt");
}
}
@Nested
@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/locks")
@TestDataPath("$PROJECT_ROOT")
@Tag("atomicfu-native")
@UseStandardTestCaseGroupProvider()
public class Locks {
@Test
public void testAllFilesPresentInLocks() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/locks"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("ReentrantLockTest.kt")
public void testReentrantLockTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/locks/ReentrantLockTest.kt");
}
@Test
@TestMetadata("SynchronizedObjectTest.kt")
public void testSynchronizedObjectTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/locks/SynchronizedObjectTest.kt");
}
}
@Nested
@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/top-level")
@TestDataPath("$PROJECT_ROOT")
@Tag("atomicfu-native")
@UseStandardTestCaseGroupProvider()
public class Top_level {
@Test
public void testAllFilesPresentInTop_level() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/top-level"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("FieldInObjectTest.kt")
public void testFieldInObjectTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/top-level/FieldInObjectTest.kt");
}
@Test
@TestMetadata("TopLevelTest.kt")
public void testTopLevelTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/top-level/TopLevelTest.kt");
}
}
@Nested
@TestMetadata("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/trace")
@TestDataPath("$PROJECT_ROOT")
@Tag("atomicfu-native")
@UseStandardTestCaseGroupProvider()
public class Trace {
@Test
public void testAllFilesPresentInTrace() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/trace"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@Test
@TestMetadata("TraceTest.kt")
public void testTraceTest() throws Exception {
runTest("plugins/atomicfu/atomicfu-compiler/testData/nativeBox/trace/TraceTest.kt");
}
}
}
@@ -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()
}
@@ -0,0 +1,130 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class IntArithmetic {
private val _x = atomic(0)
val x get() = _x.value
private val local = atomic(0)
private fun testGetValue() {
_x.value = 5
assertEquals(5, _x.value)
var aValue = _x.value
assertEquals(5, aValue)
assertEquals(5, x)
local.value = 555
aValue = local.value
assertEquals(aValue, local.value)
}
private fun testAtomicCallPlaces() {
_x.value = 5
_x.compareAndSet(5, 42)
val res = _x.compareAndSet(42, 45)
assertTrue(res)
assertTrue(_x.compareAndSet(45, 77))
assertFalse(_x.compareAndSet(95, 77))
assertTrue(_x.compareAndSet(77, 88))
}
private fun testInt() {
_x.value = 0
assertEquals(0, x)
val update = 3
assertEquals(0, _x.getAndSet(update))
assertTrue(_x.compareAndSet(update, 8))
_x.lazySet(1)
assertEquals(1, x)
assertEquals(1, _x.getAndSet(2))
assertEquals(2, x)
assertEquals(2, _x.getAndIncrement())
assertEquals(3, x)
assertEquals(3, _x.getAndDecrement())
assertEquals(2, x)
assertEquals(2, _x.getAndAdd(2))
assertEquals(4, x)
assertEquals(7, _x.addAndGet(3))
assertEquals(7, x)
assertEquals(8, _x.incrementAndGet())
assertEquals(8, x)
assertEquals(7, _x.decrementAndGet())
assertEquals(7, x)
assertTrue(_x.compareAndSet(7, 10))
}
fun test() {
testGetValue()
testAtomicCallPlaces()
testInt()
}
}
class LongArithmetic {
private val _x = atomic(4294967296)
val x get() = _x.value
private val y = atomic(5000000000)
private val z = atomic(2424920024888888848)
private val max = atomic(9223372036854775807)
fun testLong() {
assertEquals(2424920024888888848, z.value)
z.lazySet(8424920024888888848)
assertEquals(8424920024888888848, z.value)
assertEquals(8424920024888888848, z.getAndSet(8924920024888888848))
assertEquals(8924920024888888848, z.value)
assertEquals(8924920024888888849, z.incrementAndGet())
assertEquals(8924920024888888849, z.value)
assertEquals(8924920024888888849, z.getAndDecrement())
assertEquals(8924920024888888848, z.value)
assertEquals(8924920024888888848, z.getAndAdd(100000000000000000))
assertEquals(9024920024888888848, z.value)
assertEquals(-198452011965886959, z.addAndGet(-9223372036854775807))
assertEquals(-198452011965886959, z.value)
assertEquals(-198452011965886958, z.incrementAndGet())
assertEquals(-198452011965886958, z.value)
assertEquals(-198452011965886959, z.decrementAndGet())
assertEquals(-198452011965886959, z.value)
}
}
class BooleanArithmetic {
private val _x = atomic(false)
val x get() = _x.value
fun testBoolean() {
assertEquals(false, _x.value)
assertFalse(x)
_x.lazySet(true)
assertTrue(x)
assertTrue(_x.getAndSet(true))
assertTrue(_x.compareAndSet(true, false))
assertFalse(x)
}
}
class ReferenceArithmetic {
private val _x = atomic<String?>(null)
fun testReference() {
_x.value = "aaa"
assertEquals("aaa", _x.value)
_x.lazySet("bb")
assertEquals("bb", _x.value)
assertEquals("bb", _x.getAndSet("ccc"))
assertEquals("ccc", _x.value)
}
}
@Test
fun test() {
val intClass = IntArithmetic()
intClass.test()
val longClass = LongArithmetic()
longClass.testLong()
val booleanClass = BooleanArithmetic()
booleanClass.testBoolean()
val refClass = ReferenceArithmetic()
refClass.testReference()
}
@@ -0,0 +1,109 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class AtomicArrayTest {
fun testIntArray() {
val A = AtomicArrayClass()
assertTrue(A.intArr[0].compareAndSet(0, 3))
assertEquals(0, A.intArr[1].value)
A.intArr[0].lazySet(5)
assertEquals(5, A.intArr[0].value + A.intArr[1].value + A.intArr[2].value)
assertTrue(A.intArr[0].compareAndSet(5, 10))
assertEquals(10, A.intArr[0].getAndDecrement())
assertEquals(9, A.intArr[0].value)
A.intArr[2].value = 2
assertEquals(2, A.intArr[2].value)
assertTrue(A.intArr[2].compareAndSet(2, 34))
assertEquals(34, A.intArr[2].value)
}
fun testLongArray() {
val A = AtomicArrayClass()
A.longArr[0].value = 2424920024888888848
assertEquals(2424920024888888848, A.longArr[0].value)
A.longArr[0].lazySet(8424920024888888848)
assertEquals(8424920024888888848, A.longArr[0].value)
val ac = A.longArr[0].value
A.longArr[3].value = ac
assertEquals(8424920024888888848, A.longArr[3].getAndSet(8924920024888888848))
assertEquals(8924920024888888848, A.longArr[3].value)
val ac1 = A.longArr[3].value
A.longArr[4].value = ac1
assertEquals(8924920024888888849, A.longArr[4].incrementAndGet())
assertEquals(8924920024888888849, A.longArr[4].value)
assertEquals(8924920024888888849, A.longArr[4].getAndDecrement())
assertEquals(8924920024888888848, A.longArr[4].value)
A.longArr[4].value = 8924920024888888848
assertEquals(8924920024888888848, A.longArr[4].getAndAdd(100000000000000000))
val ac2 = A.longArr[4].value
A.longArr[1].value = ac2
assertEquals(9024920024888888848, A.longArr[1].value)
assertEquals(-198452011965886959, A.longArr[1].addAndGet(-9223372036854775807))
assertEquals(-198452011965886959, A.longArr[1].value)
assertEquals(-198452011965886958, A.longArr[1].incrementAndGet())
assertEquals(-198452011965886958, A.longArr[1].value)
assertEquals(-198452011965886959, A.longArr[1].decrementAndGet())
assertEquals(-198452011965886959, A.longArr[1].value)
}
fun testBooleanArray() {
val A = AtomicArrayClass()
assertFalse(A.booleanArr[1].value)
assertTrue(A.booleanArr[1].compareAndSet(false, true))
A.booleanArr[0].lazySet(true)
assertFalse(A.booleanArr[2].getAndSet(true))
assertTrue(A.booleanArr[0].value && A.booleanArr[1].value && A.booleanArr[2].value)
A.booleanArr[0].value = false
assertFalse(A.booleanArr[0].value)
}
fun testRefArray() {
val A = AtomicArrayClass()
val a2 = ARef(2)
val a3 = ARef(3)
A.refArr[0].value = a2
assertEquals(2, A.refArr[0].value!!.n)
assertTrue(A.refArr[0].compareAndSet(a2, a3))
assertEquals(3, A.refArr[0].value!!.n)
val r0 = A.refArr[0].value
A.refArr[3].value = r0
assertEquals(3, A.refArr[3].value!!.n)
val a = A.a.value
assertTrue(A.refArr[3].compareAndSet(a3, a))
}
fun testAnyArray() {
val A = AtomicArrayClass()
val s1 = "aaa"
val s2 = "bbb"
A.anyArr[0].value = s1
assertEquals("aaa", A.anyArr[0].value)
assertTrue(A.anyArr[0].compareAndSet(s1, s2))
assertEquals("bbb", A.anyArr[0].value)
val r0 = A.anyArr[0].value
A.anyArr[3].value = r0
assertEquals("bbb", A.anyArr[3].value)
}
}
private class AtomicArrayClass {
val intArr = AtomicIntArray(10)
val longArr = AtomicLongArray(10)
val booleanArr = AtomicBooleanArray(10)
val refArr = atomicArrayOfNulls<ARef>(10)
val anyArr = atomicArrayOfNulls<Any?>(10)
internal val a = atomic(ARef(8))
}
data class ARef(val n: Int)
@Test
fun test() {
val testClass = AtomicArrayTest()
testClass.testIntArray()
testClass.testLongArray()
testClass.testBooleanArray()
testClass.testRefArray()
testClass.testAnyArray()
}
@@ -0,0 +1,32 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class IndexArrayElementGetterTest {
private val clazz = AtomicArrayClass()
fun fib(a: Int): Int = if (a == 0 || a == 1) a else fib(a - 1) + fib(a - 2)
fun testIndexArrayElementGetting() {
clazz.intArr[8].value = 3
val i = fib(4)
val j = fib(5)
assertEquals(3, clazz.intArr[i + j].value)
assertEquals(3, clazz.intArr[fib(4) + fib(5)].value)
clazz.longArr[3].value = 100
assertEquals(100, clazz.longArr[fib(6) - fib(5)].value)
assertEquals(100, clazz.longArr[(fib(6) + fib(4)) % 8].value)
assertEquals(100, clazz.longArr[(fib(6) + fib(4)) % 8].value)
assertEquals(100, clazz.longArr[(fib(4) + fib(5)) % fib(5)].value)
}
private class AtomicArrayClass {
val intArr = AtomicIntArray(10)
val longArr = AtomicLongArray(10)
}
}
@Test
fun box() {
val testClass = IndexArrayElementGetterTest()
testClass.testIndexArrayElementGetting()
}
@@ -0,0 +1,33 @@
import kotlinx.atomicfu.*
import kotlin.test.*
private class AAA {
private val _counter = atomic(5L)
val counterValue: Long get() = _counter.value
val delegateCounterValue by _counter
val lateInitInt: AtomicInt
val intArr: AtomicIntArray
// test ensures that transformation does not change the order of initialization
init {
lateInitInt = atomic(10)
assertTrue(lateInitInt.compareAndSet(10, 100))
assertEquals(100, lateInitInt.value)
intArr = AtomicIntArray(10)
intArr[0].value = 10
assertTrue(intArr[0].compareAndSet(10, 100))
intArr[1].value = 20
}
init {
assertEquals(5L, _counter.value)
assertEquals(5L,counterValue)
assertEquals(5L, delegateCounterValue)
assertEquals(120, intArr[0].value + intArr[1].value)
}
}
@Test
fun box() {
val intClass = AAA()
}
@@ -0,0 +1,38 @@
import kotlinx.atomicfu.*
import kotlinx.atomicfu.locks.*
import kotlin.test.*
class LateinitPropertiesTest {
private val a: AtomicInt
private val head: AtomicRef<String>
private val dataRef: AtomicRef<Data>
private val lateIntArr: AtomicIntArray
private val lateRefArr: AtomicArray<String?>
private class Data(val n: Int)
init {
a = atomic(0)
head = atomic("AAA")
lateIntArr = AtomicIntArray(55)
val data = Data(77)
dataRef = atomic(data)
val size = 10
lateRefArr = atomicArrayOfNulls<String?>(size)
}
fun test() {
assertEquals(0, a.value)
assertTrue(head.compareAndSet("AAA", "BBB"))
assertEquals("BBB", head.value)
assertEquals(0, lateIntArr[35].value)
assertEquals(77, dataRef.value.n)
assertEquals(null, lateRefArr[5].value)
}
}
@Test
fun box() {
val testClass = LateinitPropertiesTest()
testClass.test()
}
@@ -0,0 +1,61 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LockFreeLongCounterTest {
private inline fun testWith(g: LockFreeLongCounter.() -> Long) {
val c = LockFreeLongCounter()
assertEquals(0L, c.g())
assertEquals(1L, c.increment())
assertEquals(1L, c.g())
assertEquals(2L, c.increment())
assertEquals(2L, c.g())
}
fun testBasic() = testWith { get() }
fun testGetInner() = testWith { getInner() }
fun testAdd2() {
val c = LockFreeLongCounter()
c.add2()
assertEquals(2L, c.get())
c.add2()
assertEquals(4L, c.get())
}
fun testSetM2() {
val c = LockFreeLongCounter()
c.setM2()
assertEquals(-2L, c.get())
}
}
class LockFreeLongCounter {
private val counter = atomic(0L)
fun get(): Long = counter.value
fun increment(): Long = counter.incrementAndGet()
fun add2() = counter.getAndAdd(2)
fun setM2() {
counter.value = -2L // LDC instruction here
}
fun getInner(): Long = Inner().getFromOuter()
// testing how an inner class can get access to it
private inner class Inner {
fun getFromOuter(): Long = counter.value
}
}
@Test
fun box() {
val testClass = LockFreeLongCounterTest()
testClass.testBasic()
testClass.testAdd2()
testClass.testSetM2()
testClass.testGetInner()
}
@@ -0,0 +1,55 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LockFreeQueueTest {
fun testBasic() {
val q = LockFreeQueue()
assertEquals(-1, q.dequeue())
q.enqueue(42)
assertEquals(42, q.dequeue())
assertEquals(-1, q.dequeue())
q.enqueue(1)
q.enqueue(2)
assertEquals(1, q.dequeue())
assertEquals(2, q.dequeue())
assertEquals(-1, q.dequeue())
}
}
// MS-queue
public class LockFreeQueue {
private val head = atomic(Node(0))
private val tail = atomic(head.value)
private class Node(val value: Int) {
val next = atomic<Node?>(null)
}
public fun enqueue(value: Int) {
val node = Node(value)
tail.loop { curTail ->
val curNext = curTail.next.value
if (curNext != null) {
tail.compareAndSet(curTail, curNext)
return@loop
}
if (curTail.next.compareAndSet(null, node)) {
tail.compareAndSet(curTail, node)
return
}
}
}
public fun dequeue(): Int {
head.loop { curHead ->
val next = curHead.next.value ?: return -1
if (head.compareAndSet(curHead, next)) return next.value
}
}
}
@Test
fun box() {
val testClass = LockFreeQueueTest()
testClass.testBasic()
}
@@ -0,0 +1,70 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LockFreeStackTest {
fun testClear() {
val s = LockFreeStack<String>()
assertTrue(s.isEmpty())
s.pushLoop("A")
assertTrue(!s.isEmpty())
s.clear()
assertTrue(s.isEmpty())
}
fun testPushPopLoop() {
val s = LockFreeStack<String>()
assertTrue(s.isEmpty())
s.pushLoop("A")
assertTrue(!s.isEmpty())
assertEquals("A", s.popLoop())
assertTrue(s.isEmpty())
}
fun testPushPopUpdate() {
val s = LockFreeStack<String>()
assertTrue(s.isEmpty())
s.pushUpdate("A")
assertTrue(!s.isEmpty())
assertEquals("A", s.popUpdate())
assertTrue(s.isEmpty())
}
}
class LockFreeStack<T> {
private val top = atomic<Node<T>?>(null)
private class Node<T>(val value: T, val next: Node<T>?)
fun isEmpty() = top.value == null
fun clear() { top.value = null }
fun pushLoop(value: T) {
top.loop { cur ->
val upd = Node(value, cur)
if (top.compareAndSet(cur, upd)) return
}
}
fun popLoop(): T? {
top.loop { cur ->
if (cur == null) return null
if (top.compareAndSet(cur, cur.next)) return cur.value
}
}
fun pushUpdate(value: T) {
top.update { cur -> Node(value, cur) }
}
fun popUpdate(): T? =
top.getAndUpdate { cur -> cur?.next } ?.value
}
@Test
fun box() {
val testClass = LockFreeStackTest()
testClass.testClear()
testClass.testPushPopLoop()
testClass.testPushPopUpdate()
}
@@ -0,0 +1,128 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class LoopTest {
private val a = atomic(0)
private val a1 = atomic(1)
private val b = atomic(true)
private val l = atomic(5000000000L)
private val r = atomic<A>(A("aaaa"))
private val rs = atomic<String>("bbbb")
class A(val s: String)
fun atomicfuIntLoopTest() {
a.loop { value ->
if (a.compareAndSet(value, 777)) {
assertEquals(777, a.value)
return
}
}
}
fun atomicfuBooleanLoopTest() {
b.loop { value ->
assertTrue(value)
if (!b.value) return
if (b.compareAndSet(value, false)) {
return
}
}
}
fun atomicfuLongLoopTest() {
l.loop { cur ->
if (l.compareAndSet(5000000003, 9000000000)) {
return
} else {
l.incrementAndGet()
}
}
}
fun atomicfuRefLoopTest() {
r.loop { cur ->
assertEquals("aaaa", cur.s)
if (r.compareAndSet(cur, A("bbbb"))) {
return
}
}
}
fun atomicfuLoopTest() {
atomicfuIntLoopTest()
assertEquals(777, a.value)
atomicfuBooleanLoopTest()
assertFalse(b.value)
atomicfuLongLoopTest()
assertEquals(9000000000, l.value)
atomicfuRefLoopTest()
assertEquals("bbbb", r.value.s)
}
fun atomicfuUpdateTest() {
a.value = 0
a.update { value ->
val newValue = value + 1000
if (newValue >= 0) Int.MAX_VALUE else newValue
}
assertEquals(Int.MAX_VALUE, a.value)
b.update { true }
assertEquals(true, b.value)
assertTrue(b.value)
l.value = 0L
l.update { cur ->
val newValue = cur + 1000
if (newValue >= 0L) Long.MAX_VALUE else newValue
}
assertEquals(Long.MAX_VALUE, l.value)
r.lazySet(A("aaaa"))
r.update { cur ->
A("cccc${cur.s}")
}
assertEquals("ccccaaaa", r.value.s)
}
fun atomicfuUpdateAndGetTest() {
val res1 = a.updateAndGet { value ->
if (value >= 0) Int.MAX_VALUE else value
}
assertEquals(Int.MAX_VALUE, res1)
assertEquals(true, b.updateAndGet { true })
assertEquals(Long.MAX_VALUE, l.updateAndGet { cur ->
if (cur >= 0L) Long.MAX_VALUE else cur
})
r.lazySet(A("aaaa"))
val res3 = r.updateAndGet { cur ->
A("cccc${cur.s}")
}
assertEquals("ccccaaaa", res3.s)
}
fun atomicfuGetAndUpdateTest() {
a.getAndUpdate { value ->
if (value >= 0) Int.MAX_VALUE else value
}
assertEquals(Int.MAX_VALUE, a.value)
b.getAndUpdate { true }
assertTrue(b.value)
l.getAndUpdate { cur ->
if (cur >= 0L) Long.MAX_VALUE else cur
}
assertEquals(Long.MAX_VALUE, l.value)
r.lazySet(A("aaaa"))
r.getAndUpdate { cur ->
A("cccc${cur.s}")
}
assertEquals("ccccaaaa", r.value.s)
}
}
@Test
fun box() {
val testClass = LoopTest()
testClass.atomicfuLoopTest()
testClass.atomicfuUpdateTest()
testClass.atomicfuUpdateAndGetTest()
testClass.atomicfuGetAndUpdateTest()
}
@@ -0,0 +1,30 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class MultiInitTest {
fun testBasic() {
val t = MultiInit()
check(t.incA() == 1)
check(t.incA() == 2)
check(t.incB() == 1)
check(t.incB() == 2)
}
}
class MultiInit {
private val a = atomic(0)
private val b = atomic(0)
fun incA() = a.incrementAndGet()
fun incB() = b.incrementAndGet()
companion object {
fun foo() {} // just to force some clinit in outer file
}
}
@Test
fun box() {
val testClass = MultiInitTest()
testClass.testBasic()
}
@@ -0,0 +1,45 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class AA(val value: Int) {
val b = B(value + 1)
val c = C(D(E(value + 1)))
fun updateToB(affected: Any): Boolean {
(affected as AtomicState).state.compareAndSet(this, b)
return (affected.state.value is B && (affected.state.value as B).value == value + 1)
}
fun manyProperties(affected: Any): Boolean {
(affected as AtomicState).state.compareAndSet(this, c.d.e)
return (affected.state.value is E && (affected.state.value as E).x == value + 1)
}
}
class B (val value: Int)
class C (val d: D)
class D (val e: E)
class E (val x: Int)
private class AtomicState(value: Any) {
val state = atomic<Any?>(value)
}
class ScopeTest {
fun scopeTest() {
val a = AA(0)
val affected: Any = AtomicState(a)
check(a.updateToB(affected))
val a1 = AA(0)
val affected1: Any = AtomicState(a1)
check(a1.manyProperties(affected1))
}
}
@Test
fun box() {
val testClass = ScopeTest()
testClass.scopeTest()
}
@@ -0,0 +1,36 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class SimpleLockTest {
fun withLock() {
val lock = SimpleLock()
val result = lock.withLock {
"OK"
}
assertEquals("OK", result)
}
}
class SimpleLock {
private val _locked = atomic(0)
fun <T> withLock(block: () -> T): T {
// this contrieves construct triggers Kotlin compiler to reuse local variable slot #2 for
// the exception in `finally` clause
try {
_locked.loop { locked ->
check(locked == 0)
if (!_locked.compareAndSet(0, 1)) return@loop // continue
return block()
}
} finally {
_locked.value = 0
}
}
}
@Test
fun box() {
val testClass = SimpleLockTest()
testClass.withLock()
}
@@ -0,0 +1,54 @@
import kotlinx.atomicfu.*
import kotlin.test.*
private val topLevelS = atomic<Any>(arrayOf("A", "B"))
class UncheckedCastTest {
private val s = atomic<Any>("AAA")
private val bs = atomic<Any?>(null)
@Suppress("UNCHECKED_CAST")
fun testAtomicValUncheckedCast() {
assertEquals((s as AtomicRef<String>).value, "AAA")
bs.lazySet(arrayOf(arrayOf(Box(1), Box(2))))
assertEquals((bs as AtomicRef<Array<Array<Box>>>).value[0]!![0].b * 10, 10)
}
@Suppress("UNCHECKED_CAST")
fun testTopLevelValUnchekedCast() {
assertEquals((topLevelS as AtomicRef<Array<String>>).value[1], "B")
}
private data class Box(val b: Int)
@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
private inline fun <T> AtomicRef<T>.getString(): String =
(this as AtomicRef<String>).value
fun testInlineFunc() {
assertEquals("AAA", s.getString())
}
private val a = atomicArrayOfNulls<Any?>(10)
fun testArrayValueUncheckedCast() {
a[0].value = "OK"
@Suppress("UNCHECKED_CAST")
assertEquals("OK", (a[0] as AtomicRef<String>).value)
}
fun testArrayValueUncheckedCastInlineFunc() {
a[0].value = "OK"
assertEquals("OK", a[0].getString())
}
}
@Test
fun box() {
val testClass = UncheckedCastTest()
testClass.testAtomicValUncheckedCast()
testClass.testTopLevelValUnchekedCast()
testClass.testArrayValueUncheckedCast()
testClass.testArrayValueUncheckedCastInlineFunc()
testClass.testInlineFunc()
}
@@ -0,0 +1,150 @@
import kotlinx.atomicfu.*
import kotlin.test.*
private val _topLevelInt = atomic(42)
var topLevelInt: Int by _topLevelInt
private var topLevelVolatile by atomic(56)
class DelegatedProperties {
// Delegated properties should be declared in the same scope as the original atomic values
private val _a = atomic(42)
var a: Int by _a
private var privateA: Int by _a
private val _l = atomic(55555555555)
private var l: Long by _l
private val _b = atomic(false)
private var b: Boolean by _b
private val _ref = atomic(A(B(77)))
private var ref: A by _ref
private var vInt by atomic(77)
private var vLong by atomic(777777777)
private var vBoolean by atomic(false)
private var vRef by atomic(A(B(77)))
class A (val b: B)
class B (val n: Int)
fun testDelegatedAtomicInt() {
assertEquals(42, a)
assertEquals(42, privateA)
_a.compareAndSet(42, 56)
assertEquals(56, a)
assertEquals(56, privateA)
a = 77
_a.compareAndSet(77, 66)
privateA = 88
_a.compareAndSet(88, 66)
assertEquals(66, _a.value)
assertEquals(66, a)
assertEquals(66, privateA)
val aValue = a + privateA
assertEquals(132, aValue)
}
fun testDelegatedAtomicLong() {
assertEquals(55555555555, l)
_l.getAndIncrement()
assertEquals(55555555556, l)
l = 7777777777777
assertTrue(_l.compareAndSet(7777777777777, 66666666666))
assertEquals(66666666666, _l.value)
assertEquals(66666666666, l)
}
fun testDelegatedAtomicBoolean() {
assertEquals(false, b)
_b.lazySet(true)
assertEquals(true, b)
b = false
assertTrue(_b.compareAndSet(false, true))
assertEquals(true, _b.value)
assertEquals(true, b)
}
fun testDelegatedAtomicRef() {
assertEquals(77, ref.b.n)
_ref.lazySet(A(B(66)))
assertEquals(66, ref.b.n)
assertTrue(_ref.compareAndSet(_ref.value, A(B(56))))
assertEquals(56, ref.b.n)
ref = A(B(99))
assertEquals(99, _ref.value.b.n)
}
fun testVolatileInt() {
assertEquals(77, vInt)
vInt = 55
assertEquals(110, vInt * 2)
}
fun testVolatileLong() {
assertEquals(777777777, vLong)
vLong = 55
assertEquals(55, vLong)
}
fun testVolatileBoolean() {
assertEquals(false, vBoolean)
vBoolean = true
assertEquals(true, vBoolean)
}
fun testVolatileRef() {
assertEquals(77, vRef.b.n)
vRef = A(B(99))
assertEquals(99, vRef.b.n)
}
fun testDelegatedVariablesFlow() {
_a.lazySet(55)
assertEquals(55, _a.value)
assertEquals(55, a)
var aValue = a
assertEquals(55, aValue)
}
fun test() {
testDelegatedAtomicInt()
testDelegatedAtomicLong()
testDelegatedAtomicBoolean()
testDelegatedAtomicRef()
testVolatileInt()
testVolatileBoolean()
testVolatileLong()
testVolatileRef()
testDelegatedVariablesFlow()
}
}
fun testTopLevelDelegatedProperties() {
assertEquals(42, topLevelInt)
_topLevelInt.compareAndSet(42, 56)
assertEquals(56, topLevelInt)
topLevelInt = 77
_topLevelInt.compareAndSet(77, 66)
assertEquals(66, _topLevelInt.value)
assertEquals(66, topLevelInt)
}
fun testTopLevelVolatileProperties() {
assertEquals(56, topLevelVolatile)
topLevelVolatile = 55
assertEquals(110, topLevelVolatile * 2)
}
@Test
fun box() {
val testClass = DelegatedProperties()
testClass.test()
testTopLevelDelegatedProperties()
testTopLevelVolatileProperties()
}
@@ -0,0 +1,22 @@
import kotlinx.atomicfu.locks.*
import kotlin.test.*
class ReentrantLockFieldTest {
private val lock = reentrantLock()
private var state = 0
fun testLock() {
lock.withLock {
state = 1
}
assertEquals(1, state)
assertTrue(lock.tryLock())
lock.unlock()
}
}
@Test
fun box() {
val testClass = ReentrantLockFieldTest()
testClass.testLock()
}
@@ -0,0 +1,21 @@
import kotlinx.atomicfu.locks.*
import kotlin.test.*
class SynchronizedObjectTest : SynchronizedObject() {
fun testSync() {
val result = synchronized(this) { bar() }
assertEquals("OK", result)
}
private fun bar(): String =
synchronized(this) {
"OK"
}
}
@Test
fun box() {
val testClass = SynchronizedObjectTest()
testClass.testSync()
}
@@ -0,0 +1,67 @@
import kotlinx.atomicfu.*
import kotlin.test.*
import kotlin.random.*
private object Provider {
private val port = atomic(Random.nextInt(20, 90) * 100)
fun next(): Int = port.incrementAndGet()
private val _l = atomic(2424920024888888848)
fun getL() = _l.incrementAndGet()
val _ref = atomic<String?>(null)
val _x = atomic(false)
val intArr = AtomicIntArray(10)
val longArr = AtomicLongArray(10)
val refArr = atomicArrayOfNulls<Any?>(5)
}
private object DelegatedProvider {
internal val _a = atomic(42)
var a: Int by _a
var vInt by atomic(77)
}
private fun testFieldInObject() {
val port = Provider.next()
assertEquals(port + 1, Provider.next())
assertEquals(2424920024888888849, Provider.getL())
Provider._ref.compareAndSet(null, "abc")
assertEquals("abc", Provider._ref.value)
assertFalse(Provider._x.value)
Provider.intArr[8].value = 454
assertEquals(455, Provider.intArr[8].incrementAndGet())
Provider.longArr[8].value = 4544096409680468
assertEquals(4544096409680470, Provider.longArr[8].addAndGet(2))
Provider.refArr[1].value = Provider._ref.value
assertEquals("abc", Provider.refArr[1].value)
}
private fun testDelegatedPropertiesInObject() {
assertEquals(42, DelegatedProvider.a)
DelegatedProvider._a.compareAndSet(42, 56)
assertEquals(56, DelegatedProvider.a)
DelegatedProvider.a = 77
DelegatedProvider._a.compareAndSet(77, 66)
assertEquals(66, DelegatedProvider._a.value)
assertEquals(66, DelegatedProvider.a)
assertEquals(77, DelegatedProvider.vInt)
DelegatedProvider.vInt = 55
assertEquals(110, DelegatedProvider.vInt * 2)
}
@Test
fun box() {
testFieldInObject()
testDelegatedPropertiesInObject()
}
@@ -0,0 +1,182 @@
import kotlinx.atomicfu.*
import kotlin.test.*
private val a = atomic(0)
private val b = atomic(2424920024888888848)
private val c = atomic(true)
private val abcNode = atomic(ANode(BNode(CNode(8))))
private val any = atomic<Any?>(null)
internal val a_internal = atomic(0)
internal val b_internal = atomic(2424920024888888848)
internal val c_internal = atomic(true)
internal val abcNode_internal = atomic(ANode(BNode(CNode(8))))
internal val any_internal = atomic<Any?>(null)
private val intArr = AtomicIntArray(3)
private val longArr = AtomicLongArray(5)
private val booleanArr = AtomicBooleanArray(4)
private val refArr = atomicArrayOfNulls<ANode<BNode<CNode>>>(5)
private val anyRefArr = atomicArrayOfNulls<Any>(10)
private val stringAtomicNullArr = atomicArrayOfNulls<String>(10)
class TopLevelPrimitiveTest {
fun testTopLevelInt() {
assertEquals(0, a.value)
assertEquals(0, a.getAndSet(3))
assertTrue(a.compareAndSet(3, 8))
a.lazySet(1)
assertEquals(1, a.value)
assertEquals(1, a.getAndSet(2))
assertEquals(2, a.value)
assertEquals(2, a.getAndIncrement())
assertEquals(3, a.value)
assertEquals(3, a.getAndDecrement())
assertEquals(2, a.value)
assertEquals(2, a.getAndAdd(2))
assertEquals(4, a.value)
assertEquals(7, a.addAndGet(3))
assertEquals(7, a.value)
assertEquals(8, a.incrementAndGet())
assertEquals(8, a.value)
assertEquals(7, a.decrementAndGet())
assertEquals(7, a.value)
assertTrue(a.compareAndSet(7, 10))
}
fun testTopLevelLong() {
assertEquals(2424920024888888848, b.value)
b.lazySet(8424920024888888848)
assertEquals(8424920024888888848, b.value)
assertEquals(8424920024888888848, b.getAndSet(8924920024888888848))
assertEquals(8924920024888888848, b.value)
assertEquals(8924920024888888849, b.incrementAndGet())
assertEquals(8924920024888888849, b.value)
assertEquals(8924920024888888849, b.getAndDecrement())
assertEquals(8924920024888888848, b.value)
assertEquals(8924920024888888848, b.getAndAdd(100000000000000000))
assertEquals(9024920024888888848, b.value)
assertEquals(-198452011965886959, b.addAndGet(-9223372036854775807))
assertEquals(-198452011965886959, b.value)
assertEquals(-198452011965886958, b.incrementAndGet())
assertEquals(-198452011965886958, b.value)
assertEquals(-198452011965886959, b.decrementAndGet())
assertEquals(-198452011965886959, b.value)
}
fun testTopLevelBoolean() {
assertTrue(c.value)
c.lazySet(false)
assertFalse(c.value)
assertTrue(!c.getAndSet(true))
assertTrue(c.compareAndSet(true, false))
assertFalse(c.value)
}
fun testTopLevelRef() {
assertEquals(8, abcNode.value.b.c.d)
val newNode = ANode(BNode(CNode(76)))
assertEquals(8, abcNode.getAndSet(newNode).b.c.d)
assertEquals(76, abcNode.value.b.c.d)
val l = IntArray(4){i -> i}
any.lazySet(l)
assertEquals(2, (any.value as IntArray)[2])
}
fun testTopLevelArrayOfNulls() {
assertEquals(null, stringAtomicNullArr[0].value)
assertTrue(stringAtomicNullArr[0].compareAndSet(null, "aa"))
stringAtomicNullArr[1].lazySet("aa")
assertTrue(stringAtomicNullArr[0].value == stringAtomicNullArr[1].value)
}
}
class TopLevelArrayTest {
fun testIntArray() {
assertTrue(intArr[0].compareAndSet(0, 3))
assertEquals(0, intArr[1].value)
intArr[0].lazySet(5)
assertEquals(5, intArr[0].value + intArr[1].value + intArr[2].value)
assertTrue(intArr[0].compareAndSet(5, 10))
assertEquals(10, intArr[0].getAndDecrement())
assertEquals(9, intArr[0].value)
intArr[2].value = 2
assertEquals(2, intArr[2].value)
assertTrue(intArr[2].compareAndSet(2, 34))
assertEquals(34, intArr[2].value)
}
fun testLongArray() {
longArr[0].value = 2424920024888888848
assertEquals(2424920024888888848, longArr[0].value)
longArr[0].lazySet(8424920024888888848)
assertEquals(8424920024888888848, longArr[0].value)
val ac = longArr[0].value
longArr[3].value = ac
assertEquals(8424920024888888848, longArr[3].getAndSet(8924920024888888848))
assertEquals(8924920024888888848, longArr[3].value)
val ac1 = longArr[3].value
longArr[4].value = ac1
assertEquals(8924920024888888849, longArr[4].incrementAndGet())
assertEquals(8924920024888888849, longArr[4].value)
assertEquals(8924920024888888849, longArr[4].getAndDecrement())
assertEquals(8924920024888888848, longArr[4].value)
longArr[4].value = 8924920024888888848
assertEquals(8924920024888888848, longArr[4].getAndAdd(100000000000000000))
val ac2 = longArr[4].value
longArr[1].value = ac2
assertEquals(9024920024888888848, longArr[1].value)
assertEquals(-198452011965886959, longArr[1].addAndGet(-9223372036854775807))
assertEquals(-198452011965886959, longArr[1].value)
assertEquals(-198452011965886958, longArr[1].incrementAndGet())
assertEquals(-198452011965886958, longArr[1].value)
assertEquals(-198452011965886959, longArr[1].decrementAndGet())
assertEquals(-198452011965886959, longArr[1].value)
}
fun testBooleanArray() {
assertFalse(booleanArr[1].value)
booleanArr[1].compareAndSet(false, true)
booleanArr[0].lazySet(true)
assertFalse(booleanArr[2].getAndSet(true))
assertTrue(booleanArr[0].value && booleanArr[1].value && booleanArr[2].value)
}
@Suppress("UNCHECKED_CAST")
fun testRefArray() {
val a2 = ANode(BNode(CNode(2)))
val a3 = ANode(BNode(CNode(3)))
refArr[0].value = a2
assertEquals(2, refArr[0].value!!.b.c.d)
assertTrue(refArr[0].compareAndSet(a2, a3))
assertEquals(3, refArr[0].value!!.b.c.d)
val r0 = refArr[0].value
refArr[3].value = r0
assertEquals(3, refArr[3].value!!.b.c.d)
val a = abcNode.value
assertTrue(refArr[3].compareAndSet(a3, a))
}
}
data class ANode<T>(val b: T)
data class BNode<T>(val c: T)
data class CNode(val d: Int)
@Test
fun box() {
val primitiveTest = TopLevelPrimitiveTest()
primitiveTest.testTopLevelInt()
primitiveTest.testTopLevelLong()
primitiveTest.testTopLevelBoolean()
primitiveTest.testTopLevelRef()
primitiveTest.testTopLevelArrayOfNulls()
val arrayTest = TopLevelArrayTest()
arrayTest.testIntArray()
arrayTest.testLongArray()
arrayTest.testBooleanArray()
arrayTest.testRefArray()
}
@@ -0,0 +1,106 @@
import kotlinx.atomicfu.*
import kotlin.test.*
class TraceTest {
private val defaultTrace = Trace()
private val a1 = atomic(5, defaultTrace)
private val traceWithSize = Trace(30)
private val a2 = atomic(1, traceWithSize)
private val traceWithFormat = Trace(format = TraceFormat { i, text -> "[$i: $text]" })
private val a = atomic(0, traceWithFormat)
private val traceWithSizeAndFormat = Trace(30, TraceFormat { id, text -> "$id: $text"})
private val a3 = atomic(2)
private val shortTrace = Trace(4)
private val s = atomic(0, shortTrace.named("s"))
fun testDefaultTrace() {
val oldValue = a1.value
defaultTrace { "before CAS value = $oldValue" }
val res = a1.compareAndSet(oldValue, oldValue * 10)
val newValue = a1.value
defaultTrace { "after CAS value = $newValue" }
}
fun testTraceWithSize() {
val oldValue = a2.value
traceWithSize { "before CAS value = $oldValue" }
assertTrue(a2.compareAndSet(oldValue, oldValue * 10))
traceWithSize { "after CAS value = ${a2.value}" }
traceWithSize { "before getAndDecrement value = ${a2.value}" }
a2.getAndDecrement()
assertEquals(9, a2.value)
traceWithSize { "after getAndDecrement value = ${a2.value}" }
}
fun testTraceWithFormat() {
val oldValue = a3.value
traceWithFormat { "before CAS value = $oldValue" }
assertTrue(a3.compareAndSet(oldValue, oldValue * 10))
traceWithFormat { "after CAS value = ${a3.value}" }
traceWithFormat { "before getAndDecrement value = ${a3.value}" }
a3.getAndDecrement()
assertEquals(19, a3.value)
traceWithFormat { "after getAndDecrement value = ${a3.value}" }
}
fun testNamedTrace() {
s.value = 5
shortTrace { "before CAS value = ${s.value}" }
s.compareAndSet(5, -2)
assertEquals(-2, s.value)
shortTrace { "after CAS value = ${s.value}" }
}
private enum class Status { START, END }
fun testMultipleAppend() {
val i = 1
traceWithFormat.append(i, Status.START)
assertEquals(0, a.value)
a.incrementAndGet()
traceWithFormat.append(i, a.value, "incAndGet")
assertEquals(1, a.value)
a.lazySet(10)
traceWithFormat.append(i, a.value, "lazySet")
assertEquals(10, a.value)
traceWithFormat.append(i, Status.END)
}
fun testTraceInBlock() {
a1.lazySet(5)
if (a1.value == 5) {
defaultTrace { "Value checked" }
if (a1.compareAndSet(5, 10)) {
defaultTrace { "CAS succeeded" }
}
}
assertEquals(10, a1.value)
while (true) {
if (a1.value == 10) {
defaultTrace.append("Value checked", a1.value)
a1.value = 15
break
} else {
defaultTrace.append("Wrong value", a1.value)
}
}
}
fun test() {
testDefaultTrace()
testTraceWithSize()
testTraceWithFormat()
testNamedTrace()
testMultipleAppend()
testTraceInBlock()
}
}
@Test
fun box() {
TraceTest().test()
}
@@ -12,6 +12,7 @@ private enum class TestProperty(shortName: String) {
// effect on other Gradle tasks (ex: :kotlin-native:dist) that might be executed along with test task.
KOTLIN_NATIVE_HOME("nativeHome"),
COMPILER_CLASSPATH("compilerClasspath"),
COMPILER_PLUGINS("compilerPlugins"),
CUSTOM_KLIBS("customKlibs"),
TEST_TARGET("target"),
TEST_MODE("mode"),
@@ -77,12 +78,23 @@ private class ComputedTestProperties(private val task: Test) {
private fun Test.ComputedTestProperties(init: ComputedTestProperties.() -> Unit): ComputedTestProperties =
ComputedTestProperties(this).apply { init() }
/**
* @param taskName Name of Gradle task.
* @param tag Optional JUnit test tag. See https://junit.org/junit5/docs/current/user-guide/#writing-tests-tagging-and-filtering
* @param requirePlatformLibs Where platform KLIBs from the Kotlin/Native distribution are required for running this test.
* @param customCompilerDependencies The [Configuration]s that provide additional JARs to be added to the compiler's classpath.
* @param customTestDependencies The [Configuration]s that provide KLIBs to be added to Kotlin/Native compiler dependencies list
* along with Kotlin/Native stdlib KLIB and Kotlin/Native platform KLIBs (the latter only if [requirePlatformLibs] is `true`).
* @param compilerPluginDependencies The [Configuration]s that provide compiler plugins to be enabled for the Kotlin/Native compiler
* for the duration of test execution.
*/
fun Project.nativeTest(
taskName: String,
tag: String?,
requirePlatformLibs: Boolean = false,
customDependencies: List<Configuration> = emptyList(),
customKlibDependencies: List<Configuration> = emptyList()
customCompilerDependencies: List<Configuration> = emptyList(),
customTestDependencies: List<Configuration> = emptyList(),
compilerPluginDependencies: List<Configuration> = emptyList()
) = projectTest(
taskName,
jUnitMode = JUnitMode.JUnit5,
@@ -141,7 +153,7 @@ fun Project.nativeTest(
else
null
customDependencies.forEach(::dependsOn)
customCompilerDependencies.forEach(::dependsOn)
lazyClassPath {
if (customNativeHome == null) {
@@ -151,13 +163,18 @@ fun Project.nativeTest(
this += file(customNativeHome).resolve("konan/lib/trove4j.jar")
}
customDependencies.flatMapTo(this) { it.files }
customCompilerDependencies.flatMapTo(this) { it.files }
}
}
computeLazy(COMPILER_PLUGINS) {
compilerPluginDependencies.forEach(::dependsOn)
lazyClassPath { compilerPluginDependencies.flatMapTo(this) { it.files } }
}
computeLazy(CUSTOM_KLIBS) {
customKlibDependencies.forEach(::dependsOn)
lazyClassPath { customKlibDependencies.flatMapTo(this) { it.files } }
customTestDependencies.forEach(::dependsOn)
lazyClassPath { customTestDependencies.flatMapTo(this) { it.files } }
}
// Pass Gradle properties as JVM properties so test process can read them.