[K/N] Migrate some runtime/workers tests to new testing infra ^KT-61259

This commit is contained in:
Alexander Shabalin
2023-10-30 13:15:20 +01:00
committed by Space Team
parent fd1579186f
commit 9ad7b00613
34 changed files with 547 additions and 1193 deletions
@@ -845,73 +845,18 @@ standaloneTest("worker_bound_reference0") {
}
}
tasks.register("worker0", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/worker0.kt"
}
tasks.register("worker1", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/worker1.kt"
}
tasks.register("worker2", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/worker2.kt"
}
tasks.register("worker3", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/worker3.kt"
}
tasks.register("worker4", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/worker4.kt"
}
// This tests changes main thread worker queue state, so better be executed alone.
standaloneTest("worker5") {
useGoldenData = true
source = "runtime/workers/worker5.kt"
}
tasks.register("worker6", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/worker6.kt"
}
tasks.register("worker7", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/worker7.kt"
}
tasks.register("worker8", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/worker8.kt"
}
tasks.register("worker9_experimentalMM", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/worker9_experimentalMM.kt"
}
tasks.register("worker10", KonanLocalTest) {
enabled = !isNoopGC
useGoldenData = true
source = "runtime/workers/worker10.kt"
}
tasks.register("worker11", KonanLocalTest) {
enabled = !isAggressiveGC // TODO: Investigate why too slow
useGoldenData = true
source = "runtime/workers/worker11.kt"
}
tasks.register("worker_exception_messages", KonanLocalTest) {
source = "runtime/workers/worker_exception_messages.kt"
}
standaloneTest("worker_exceptions") {
flags = ["-tr", "-Xworker-exception-handling=use-hook"]
outputChecker = {
@@ -998,10 +943,6 @@ standaloneTest("worker_threadlocal_no_leak") {
source = "runtime/workers/worker_threadlocal_no_leak.kt"
}
tasks.register("worker_list_workers", KonanLocalTest) {
source = "runtime/workers/worker_list_workers.kt"
}
standaloneTest("freeze_disabled") {
enabled = !isNoopGC
flags = ["-tr"]
@@ -1009,16 +950,6 @@ standaloneTest("freeze_disabled") {
testLogger = KonanTest.Logger.SILENT
}
tasks.register("lazy0", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/lazy0.kt"
}
tasks.register("lazy1", KonanLocalTest) {
useGoldenData = true
source = "runtime/workers/lazy1.kt"
}
standaloneTest("lazy2") {
useGoldenData = true
source = "runtime/workers/lazy2.kt"
@@ -1029,11 +960,6 @@ standaloneTest("lazy3") {
source = "runtime/workers/lazy3.kt"
}
tasks.register("lazy4", KonanLocalTest) {
enabled = !isAggressiveGC // TODO: Investigate why too slow
source = "runtime/workers/lazy4.kt"
}
tasks.register("mutableData1", KonanLocalTest) {
source = "runtime/workers/mutableData1.kt"
}
@@ -1,89 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
package runtime.workers.lazy0
import kotlin.test.*
import kotlin.native.concurrent.*
data class Data(val x: Int, val y: String)
object Immutable {
val x by atomicLazy {
42
}
}
object Immutable2 {
val y by atomicLazy {
Data(239, "Kotlin")
}
}
object Immutable3 {
val x by lazy {
var result = 0
for (i in 1 .. 1000)
result += i
result
}
}
fun testSingleData(workers: Array<Worker>) {
val set = mutableSetOf<Any?>()
for (attempt in 1 .. 3) {
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].execute(TransferMode.SAFE, { "" }) { _ -> Immutable2.y }
})
futures.forEach { set += it.result }
}
assertEquals(set.size, 1)
assertEquals(set.single(), Immutable2.y)
}
fun testFrozenLazy(workers: Array<Worker>) {
// To make sure it is always frozen, and we don't race in relaxed mode.
Immutable3.freeze()
val set = mutableSetOf<Int>()
for (attempt in 1 .. 3) {
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].execute(TransferMode.SAFE, { "" }) { _ -> Immutable3.x }
})
futures.forEach { set += it.result }
}
assertEquals(1, set.size)
assertEquals(Immutable3.x, set.single())
assertEquals(1001 * 500, set.single())
}
fun testLiquidLazy() {
class L {
val value by lazy {
17
}
}
val l1 = L()
for (i in 1 .. 100)
assertEquals(l1.value, 17)
val l2 = L()
l2.freeze()
for (i in 1 .. 100)
assertEquals(l2.value, 17)
}
@Test fun runTest() {
assertEquals(42, Immutable.x)
val COUNT = 5
val workers = Array(COUNT, { _ -> Worker.start()})
testSingleData(workers)
testFrozenLazy(workers)
testLiquidLazy()
println("OK")
}
@@ -1 +0,0 @@
OK
@@ -1,103 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, FreezingIsDeprecated::class)
package runtime.workers.lazy1
import kotlin.test.*
import kotlin.native.concurrent.*
private var y = 20
class Lazy(mode: LazyThreadSafetyMode) {
val x = 17
val self by lazy(mode) { this }
val recursion: Int by lazy(mode) {
if (x < 17) 42 else recursion
}
val finiteRecursion : Int by lazy(mode) {
if (y < 17) 42 else {
y -= 1
finiteRecursion + 1
}
}
val freezer: Int by lazy(mode) {
freeze()
42
}
val thrower: String by lazy(mode) {
if (x < 100) throw IllegalArgumentException()
"FAIL"
}
}
private val checkedLazyModes =
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL)
listOf(LazyThreadSafetyMode.PUBLICATION)
else
listOf(LazyThreadSafetyMode.SYNCHRONIZED, LazyThreadSafetyMode.PUBLICATION)
@Test fun runTest1() {
for (mode in checkedLazyModes) {
// We decided to synchonaize behaviour to be consistent with jvm version.
// Anyway, it's doesn't looks like well-defined case
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
val expected = if (mode == LazyThreadSafetyMode.SYNCHRONIZED) 46 else 42
y = 20
assertEquals(Lazy(mode).finiteRecursion, expected)
y = 20
assertEquals(Lazy(mode).freeze().finiteRecursion, expected)
} else {
assertFailsWith<IllegalStateException> {
println(Lazy(mode).recursion)
}
assertFailsWith<IllegalStateException> {
println(Lazy(mode).freeze().recursion)
}
y = 20
assertFailsWith<IllegalStateException> {
println(Lazy(mode).finiteRecursion)
}
y = 20
assertFailsWith<IllegalStateException> {
println(Lazy(mode).freeze().finiteRecursion)
}
}
}
}
@Test fun runTest2() {
for (mode in checkedLazyModes) {
var sum = 0
for (i in 1..100) {
val self = Lazy(mode).freeze()
assertEquals(self, self.self)
sum += self.self.hashCode()
}
}
println("OK")
}
@Test fun runTest3() {
if (Platform.isFreezingEnabled) {
for (mode in checkedLazyModes) {
assertFailsWith<InvalidMutabilityException> {
println(Lazy(mode).freezer)
}
}
}
}
@Test fun runTest4() {
for (mode in checkedLazyModes) {
val self = Lazy(mode)
repeat(10) {
assertFailsWith<IllegalArgumentException> {
println(self.thrower)
}
}
}
}
@@ -1 +0,0 @@
OK
@@ -1,77 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
package runtime.workers.lazy4
import kotlin.test.*
import kotlin.concurrent.AtomicInt
import kotlin.concurrent.*
import kotlin.native.concurrent.*
const val WORKERS_COUNT = 20
class IntHolder(val value:Int)
class C(mode: LazyThreadSafetyMode, private val initializer: () -> IntHolder) {
val data by lazy(mode) { initializer() }
}
fun concurrentLazyAccess(freeze: Boolean, mode: LazyThreadSafetyMode) {
// in old mm PUBLICATION is in fact SYNCHRONIZED, while SYNCHRONIZED is not supported
val argumentMode = if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) mode else LazyThreadSafetyMode.PUBLICATION
val initializerCallCount = AtomicInt(0)
val c = C(argumentMode) {
initializerCallCount.incrementAndGet()
IntHolder(42)
}
if (freeze) {
c.freeze()
}
val workers = Array(WORKERS_COUNT, { Worker.start() })
val inited = AtomicInt(0)
val canStart = AtomicInt(0)
val futures = Array(workers.size) { i ->
workers[i].execute(TransferMode.SAFE, { Triple(inited, canStart, c) }) { (inited, canStart, c) ->
inited.incrementAndGet()
while (canStart.value != 1) {}
c.data
}
}
while (inited.value < workers.size) {}
canStart.value = 1
val results = futures.map { it.result }
results.forEach {
assertEquals(42, it.value)
assertSame(results[0], it)
}
workers.forEach {
it.requestTermination().result
}
if (mode == LazyThreadSafetyMode.SYNCHRONIZED) {
assertEquals(1, initializerCallCount.value)
}
}
@Test
fun concurrentLazyAccessUnfrozen() {
if (Platform.memoryModel != MemoryModel.EXPERIMENTAL) {
return
}
concurrentLazyAccess(false, LazyThreadSafetyMode.SYNCHRONIZED)
concurrentLazyAccess(false, LazyThreadSafetyMode.PUBLICATION)
}
@Test
fun concurrentLazyAccessFrozen() {
concurrentLazyAccess(true, LazyThreadSafetyMode.SYNCHRONIZED)
concurrentLazyAccess(true, LazyThreadSafetyMode.PUBLICATION)
}
@@ -1,26 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(ObsoleteWorkersApi::class)
package runtime.workers.worker0
import kotlin.test.*
import kotlin.native.concurrent.*
@Test fun runTest() {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { "Input" }) {
input ->
assertEquals(1, 1)
assertFailsWith<AssertionError> { assertEquals(1, 2) }
input + " processed"
}
future.consume {
result -> println("Got $result")
}
worker.requestTermination().result
println("OK")
}
@@ -1,2 +0,0 @@
Got Input processed
OK
@@ -1,36 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(ObsoleteWorkersApi::class)
package runtime.workers.worker1
import kotlin.test.*
import kotlin.native.concurrent.*
@Test fun runTest() {
val COUNT = 5
val workers = Array(COUNT, { _ -> Worker.start()})
for (attempt in 1 .. 3) {
val futures = Array(workers.size,
{ i -> workers[i].execute(TransferMode.SAFE, { "$attempt: Input $i" })
{ input -> input + " processed" }
})
futures.forEachIndexed { index, future ->
future.consume {
result ->
if ("$attempt: Input $index processed" != result) {
println("Got unexpected $result")
throw Error(result)
}
}
}
}
workers.forEach {
it.requestTermination().result
}
println("OK")
}
@@ -1 +0,0 @@
OK
@@ -1,118 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
package runtime.workers.worker11
import kotlin.test.*
import kotlin.concurrent.AtomicInt
import kotlin.concurrent.*
import kotlin.native.concurrent.*
import kotlinx.cinterop.convert
data class Job(val index: Int, var input: Int, var counter: Int)
fun initJobs(count: Int) = Array<Job?>(count) { i -> Job(i, i * 2, i)}
@Test fun runTest0() {
val workers = Array(100, { _ -> Worker.start() })
val jobs = initJobs(workers.size)
val futures = Array(workers.size, { workerIndex ->
workers[workerIndex].execute(TransferMode.SAFE, {
val job = jobs[workerIndex]
jobs[workerIndex] = null
job!!
}) { job ->
job.counter += job.input
job
}
})
val futureSet = futures.toSet()
var consumed = 0
while (consumed < futureSet.size) {
val ready = waitForMultipleFutures(futureSet, 10000)
ready.forEach {
it.consume { job ->
assertEquals(job.index * 3, job.counter)
jobs[job.index] = job
}
consumed++
}
}
assertEquals(consumed, workers.size)
workers.forEach {
it.requestTermination().result
}
println("OK")
}
val COUNT = 2
val counters = Array(COUNT) { AtomicInt(0) }
@Test fun runTest1() {
val workers = Array(COUNT) { Worker.start() }
// Ensure processQueue() can only be called on current Worker.
workers.forEach {
assertFailsWith<IllegalStateException> {
it.processQueue()
}
}
val futures = Array(workers.size) { workerIndex ->
workers[workerIndex].execute(TransferMode.SAFE, {
workerIndex
}) {
index ->
assertEquals(0, counters[index].value)
// Process following request.
while (!Worker.current!!.processQueue()) {}
// Ensure it has an effect.
assertEquals(1, counters[index].value)
// No more non-terminating tasks in this worker queue.
assertEquals(false, Worker.current!!.processQueue())
}
}
val futures2 = Array(workers.size) { workerIndex ->
workers[workerIndex].execute(TransferMode.SAFE, {
workerIndex
}) { index ->
assertEquals(0, counters[index].value)
counters[index].incrementAndGet()
}
}
futures2.forEach { it.result }
futures.forEach { it.result }
workers.forEach {
it.requestTermination().result
}
// Ensure terminated workers are no longer there.
workers.forEach {
assertFailsWith<IllegalStateException> { it.execute(TransferMode.SAFE, { Unit }) { println("ERROR") } }
}
}
@Test fun runTest2() {
val workers = Array(COUNT) { Worker.start() }
val futures = Array(workers.size) { workerIndex ->
workers[workerIndex].execute(TransferMode.SAFE, { null }) {
// Here we processed termination request.
assertEquals(false, Worker.current.processQueue())
}
}
workers.forEach {
it.executeAfter(1000L*1000*1000, {
println("DELAY EXECUTED")
assert(false)
}.freeze())
}
workers.forEach {
it.requestTermination(processScheduledJobs = false).result
}
// Process futures, ignoring possible cancelled ones.
futures.forEach {
try { it.result } catch (e: IllegalStateException) {}
}
}
@@ -1 +0,0 @@
OK
@@ -1,46 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(ObsoleteWorkersApi::class)
package runtime.workers.worker2
import kotlin.test.*
import kotlin.native.concurrent.*
data class WorkerArgument(val intParam: Int, val stringParam: String)
data class WorkerResult(val intResult: Int, val stringResult: String)
@Test fun runTest() {
val COUNT = 5
val workers = Array(COUNT, { _ -> Worker.start()})
for (attempt in 1 .. 3) {
val futures = Array(workers.size, { workerIndex -> workers[workerIndex].execute(TransferMode.SAFE, {
WorkerArgument(workerIndex, "attempt $attempt") }) { input ->
var sum = 0
for (i in 0..input.intParam * 1000) {
sum += i
}
WorkerResult(sum, input.stringParam + " result")
}
})
val futureSet = futures.toSet()
var consumed = 0
while (consumed < futureSet.size) {
val ready = waitForMultipleFutures(futureSet, 10000)
ready.forEach {
it.consume { result ->
if (result.stringResult != "attempt $attempt result") throw Error("Unexpected $result")
consumed++
}
}
}
}
workers.forEach {
it.requestTermination().result
}
println("OK")
}
@@ -1 +0,0 @@
OK
@@ -1,37 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, ObsoleteWorkersApi::class)
package runtime.workers.worker3
import kotlin.test.*
import kotlin.native.concurrent.*
data class DataParam(var int: Int)
data class WorkerArgument(val intParam: Int, val dataParam: DataParam)
data class WorkerResult(val intResult: Int, val stringResult: String)
@Test fun runTest() {
main(emptyArray())
}
fun main(args: Array<String>) {
val worker = Worker.start()
val dataParam = DataParam(17)
val future = try {
worker.execute(TransferMode.SAFE,
{ WorkerArgument(42, dataParam) }) {
input -> WorkerResult(input.intParam, input.dataParam.toString() + " result")
}
} catch (e: IllegalStateException) {
null
}
if (future != null && Platform.memoryModel == MemoryModel.STRICT)
println("Fail 1")
if (dataParam.int != 17) println("Fail 2")
worker.requestTermination().result
println("OK")
}
@@ -1 +0,0 @@
OK
@@ -1,185 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
package runtime.workers.worker4
import kotlin.test.*
import kotlin.concurrent.AtomicInt
import kotlin.native.concurrent.*
@Test fun runTest1() {
withWorker {
val future = execute(TransferMode.SAFE, { 41 }) { input ->
input + 1
}
future.consume { result ->
println("Got $result")
}
}
println("OK")
}
@Test fun runTest2() {
withWorker {
val counter = AtomicInt(0)
executeAfter(0, {
assertTrue(Worker.current.park(10_000_000, false))
assertEquals(counter.value, 0)
assertTrue(Worker.current.processQueue())
assertEquals(1, counter.value)
// Let main proceed.
counter.incrementAndGet() // counter becomes 2 here.
assertTrue(Worker.current.park(10_000_000, true))
assertEquals(3, counter.value)
}.freeze())
executeAfter(0, {
counter.incrementAndGet()
Unit
}.freeze())
while (counter.value < 2) {
Worker.current.park(1_000)
}
executeAfter(0, {
counter.incrementAndGet()
Unit
}.freeze())
while (counter.value == 2) {
Worker.current.park(1_000)
}
}
}
@Test fun runTest3() {
val worker = Worker.start(name = "Lumberjack")
val counter = AtomicInt(0)
worker.executeAfter(0, {
assertEquals("Lumberjack", Worker.current.name)
counter.incrementAndGet()
Unit
}.freeze())
while (counter.value == 0) {
Worker.current.park(1_000)
}
assertEquals("Lumberjack", worker.name)
worker.requestTermination().result
assertFailsWith<IllegalStateException> {
println(worker.name)
}
}
@Test fun runTest4() {
val counter = AtomicInt(0)
Worker.current.executeAfter(10_000, {
counter.incrementAndGet()
Unit
}.freeze())
assertTrue(Worker.current.park(1_000_000, process = true))
assertEquals(1, counter.value)
}
@Test fun runTest5() {
val main = Worker.current
val counter = AtomicInt(0)
withWorker {
executeAfter(1000, {
main.executeAfter(1, {
counter.incrementAndGet()
Unit
}.freeze())
}.freeze())
assertTrue(main.park(1000L * 1000 * 1000, process = true))
assertEquals(1, counter.value)
}
}
@Test fun runTest6() {
// Ensure zero timeout works properly.
Worker.current.park(0, process = true)
}
@Test fun runTest7() {
val counter = AtomicInt(0)
withWorker {
val f1 = execute(TransferMode.SAFE, { counter }) { counter ->
Worker.current.park(Long.MAX_VALUE / 1000L, process = true)
counter.incrementAndGet()
}
// wait a bit
Worker.current.park(10_000L)
// submit a task
val f2 = execute(TransferMode.SAFE, { counter }) { counter ->
counter.incrementAndGet()
}
f1.consume {}
f2.consume {}
assertEquals(2, counter.value)
}
}
// This test checks that when multiple `executeAfter` jobs are submitted to `targetWorker` and have the
// same scheduled execution time (in micros since an epoch), nether of them gets lost.
@Test fun testExecuteAfterScheduledTimeClash() = withWorker {
val targetWorker = this
val mainWorker = Worker.current
// Configuration of the test.
val numberOfSubmitters = 2
val numberOfTasks = 100
val delayInMicroseconds = 100L
val submitters = Array(numberOfSubmitters) { Worker.start() }
try {
val readySubmittersCounter = AtomicInt(0)
val executedTasksCounter = AtomicInt(0)
val finishedBatchesCounter = AtomicInt(0)
submitters.forEach {
it.executeAfter(0L, {
readySubmittersCounter.incrementAndGet()
// Wait for other submitters, to make them all start at the same time:
while (readySubmittersCounter.value != numberOfSubmitters) {}
// Concurrently submit tasks with matching scheduled execution time:
repeat(numberOfTasks) {
targetWorker.executeAfter(delayInMicroseconds, {
executedTasksCounter.incrementAndGet()
Unit
}.freeze())
}
// Use larger delay for the task below, to make sure it gets executed after
// the tasks above submitted by the same worker.
// If the order is wrong, the test will fail as well.
// NOTE: the code below was affected by the same problem with clashing times, so despite all the effort
// the test still might hang without a fix.
targetWorker.executeAfter(delayInMicroseconds + 1, {
mainWorker.executeAfter(0L, {
finishedBatchesCounter.incrementAndGet()
Unit
}.freeze())
}.freeze())
}.freeze())
}
while (finishedBatchesCounter.value != numberOfSubmitters) {
// Wait and allow processing the `finishedBatchesCounter.increment()` tasks above:
Worker.current.park(delayInMicroseconds, process = true)
}
// Note: we could have just waited for the condition above to become true,
// but this would mean that the test would hang in case of failure, which is not quite convenient.
assertEquals(numberOfSubmitters * numberOfTasks, executedTasksCounter.value)
} finally {
submitters.forEach { it.requestTermination().result }
}
}
@@ -1,2 +0,0 @@
Got 42
OK
@@ -8,33 +8,8 @@ import kotlin.test.*
import kotlin.native.concurrent.*
fun runTest0() {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { "zzz" }) {
input -> input.length
}
future.consume {
result -> println("Got $result")
}
worker.requestTermination().result
println("OK")
}
var done = false
fun runTest1() {
val worker = Worker.current
done = false
// Here we request execution of the operation on the current worker.
worker.executeAfter(0, {
done = true
}.freeze())
while (!done)
worker.processQueue()
}
// Ensure that termination of current worker on main thread doesn't lead to problems.
fun runTest2() {
fun main() {
val worker = Worker.current
val future = worker.requestTermination(false)
worker.processQueue()
@@ -43,11 +18,5 @@ fun runTest2() {
// After termination request this worker is no longer addressable.
assertFailsWith<IllegalStateException> { worker.executeAfter(0, {
println("BUG!")
}.freeze()) }
}
fun main() {
runTest0()
runTest1()
runTest2()
})}
}
@@ -1,2 +0,0 @@
Got 3
OK
@@ -1,44 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
package runtime.workers.worker6
import kotlin.test.*
import kotlin.native.concurrent.*
@Test fun runTest1() {
withWorker {
val future = execute(TransferMode.SAFE, { 42 }) { input ->
input.toString()
}
future.consume { result ->
println("Got $result")
}
}
println("OK")
}
var int1 = 1
val int2 = 77
@Test fun runTest2() {
int1++
withWorker {
executeAfter(0, {
if (kotlin.native.Platform.memoryModel == kotlin.native.MemoryModel.EXPERIMENTAL) {
int1++
assertEquals(3, int1)
} else {
assertFailsWith<IncorrectDereferenceException> {
int1++
}
assertEquals(2, int1)
}
assertEquals(77, int2)
}.freeze())
}
}
@@ -1,2 +0,0 @@
Got 42
OK
@@ -1,29 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(ObsoleteWorkersApi::class)
package runtime.workers.worker7
import kotlin.test.*
import kotlin.native.concurrent.*
@Test fun runTest() {
val worker = Worker.start(false)
val future = worker.execute(TransferMode.SAFE, { "Input" }) {
input -> println(input)
}
future.consume {
result -> println("Got $result")
}
assertFailsWith<IllegalStateException> {
println(worker.execute(TransferMode.SAFE, { null }, { _ -> throw Error("An error") }).result)
}
worker.requestTermination().result
println("OK")
}
@@ -1,3 +0,0 @@
Input
Got kotlin.Unit
OK
@@ -1,33 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(FreezingIsDeprecated::class, ObsoleteWorkersApi::class, kotlinx.cinterop.ExperimentalForeignApi::class)
package runtime.workers.worker8
import kotlin.test.*
import kotlin.native.concurrent.*
data class SharedDataMember(val double: Double)
data class SharedData(val string: String, val int: Int, val member: SharedDataMember)
@Test fun runTest() {
val worker = Worker.start()
// Here we do rather strange thing. To test object detach API we detach object graph,
// pass detached graph to a worker, where we manually reattached passed value.
val future = worker.execute(TransferMode.SAFE, {
DetachedObjectGraph { SharedData("Hello", 10, SharedDataMember(0.1)) }.asCPointer()
}) {
inputDetached ->
val input = DetachedObjectGraph<SharedData>(inputDetached).attach()
println(input)
}
future.consume {
result -> println("Got $result")
}
worker.requestTermination().result
println("OK")
}
@@ -1,3 +0,0 @@
SharedData(string=Hello, int=10, member=SharedDataMember(double=0.1))
Got kotlin.Unit
OK
@@ -1,92 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
package runtime.workers.worker9_experimentalMM
import kotlin.test.*
import kotlin.native.concurrent.*
@Test fun runTest1() {
withLock { println("zzz") }
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
withLock {
println("42")
}
}
future.result
worker.requestTermination().result
println("OK")
}
fun withLock(op: () -> Unit) {
op()
}
@Test fun runTest2() {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
val me = Worker.current
var x = 1
me.executeAfter (20000) {
println("second ${++x}")
}
me.executeAfter(10000) {
println("first ${++x}")
}
}
worker.requestTermination().result
}
@Test fun runTest3() {
val worker = Worker.start()
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) {
worker.executeAfter {
println("unfrozen OK")
}
} else {
assertFailsWith<IllegalStateException> {
worker.executeAfter {
println("shall not happen")
}
}
}
assertFailsWith<IllegalArgumentException> {
worker.executeAfter(-1, {
println("shall not happen")
}.freeze())
}
worker.executeAfter(0, {
println("frozen OK")
}.freeze())
worker.requestTermination().result
}
class Node(var node: Node?, var outher: Node?)
fun makeCyclic(): Node {
val inner = Node(null, null)
inner.node = inner
val outer = Node(null, null)
inner.outher = outer
return outer
}
@OptIn(kotlin.native.runtime.NativeRuntimeApi::class)
@Test fun runTest4() {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, { }) {
makeCyclic().also {
kotlin.native.runtime.GC.collect()
}
}
assert(future.result != null)
worker.requestTermination().result
}
@@ -1,7 +0,0 @@
zzz
42
OK
first 2
second 3
unfrozen OK
frozen OK
@@ -1,104 +0,0 @@
@file:OptIn(kotlin.experimental.ExperimentalNativeApi::class, FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
package runtime.workers.worker_exception_messages
import kotlin.test.*
import kotlin.native.concurrent.*
@Test
fun checkArgumentTransferFailed(): Unit = withWorker {
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) return // Transfer is no-op in this case.
val argument = Any()
val exception = assertFailsWith<IllegalStateException> {
execute(TransferMode.SAFE, { argument }) {
}
}
assertEquals("Unable to transfer object: it is still owned elsewhere", exception.message)
}
@Test
fun checkDetachedObjectGraphTransferFailed() {
if (Platform.memoryModel == MemoryModel.EXPERIMENTAL) return // Transfer is no-op in this case.
val obj = Any()
val exception = assertFailsWith<IllegalStateException> {
DetachedObjectGraph { obj }
}
assertEquals("Unable to transfer object: it is still owned elsewhere", exception.message)
}
@Test
fun checkProcessQueueOnWrongThread(): Unit = withWorker {
val exception = assertFailsWith<IllegalStateException> {
processQueue()
}
assertEquals("Worker is not current or already terminated", exception.message)
}
@Test
fun checkParkOnWrongThread(): Unit = withWorker {
val exception = assertFailsWith<IllegalStateException> {
park(1L)
}
assertEquals("Worker is not current or already terminated", exception.message)
}
@Test
fun checkFutureConsumedTwice(): Unit = withWorker {
val future = execute(TransferMode.SAFE, {}) {
42
}
assertEquals(42, future.result)
val exception = assertFailsWith<IllegalStateException> {
future.result
}
assertEquals("Future is in an invalid state", exception.message)
}
@Test
fun checkTerminatedWorkerName() {
val worker = Worker.start(name = "WorkerName")
assertEquals("WorkerName", worker.name)
worker.requestTermination().result
val exception = assertFailsWith<IllegalStateException> {
worker.name
}
assertEquals("Worker is already terminated", exception.message)
}
@Test
fun checkTerminatedWorkerExecute() {
val worker = Worker.start()
worker.execute(TransferMode.SAFE, {}, {}).result
worker.requestTermination().result
val exception = assertFailsWith<IllegalStateException> {
worker.execute(TransferMode.SAFE, {}, {}).result
}
assertEquals("Worker is already terminated", exception.message)
}
@Test
fun checkTerminatedWorkerExecuteAfter() {
val worker = Worker.start()
worker.executeAfter(0L, {}.freeze())
worker.requestTermination().result
val exception = assertFailsWith<IllegalStateException> {
worker.executeAfter(0L, {}.freeze())
}
assertEquals("Worker is already terminated", exception.message)
}
@Test
fun checkTerminatedWorkerRequestTermination() {
val worker = Worker.start()
worker.requestTermination().result
val exception = assertFailsWith<IllegalStateException> {
worker.requestTermination()
}
assertEquals("Worker is already terminated", exception.message)
}
@@ -1,38 +0,0 @@
@file:OptIn(kotlin.ExperimentalStdlibApi::class, ObsoleteWorkersApi::class)
package runtime.workers.worker_list_workers
import kotlin.native.concurrent.*
import kotlin.test.*
const val WORKER_COUNT = 10
@Test
fun getAllWorkers() {
val workers = Array(WORKER_COUNT) { Worker.start() }
val expectedWorkers = listOf(Worker.current) + workers
assertEquals(expectedWorkers.toSet(), Worker.activeWorkers.toSet())
workers.forEach {
it.requestTermination().result
}
}
@Test
fun getActiveWorkers() {
val workers = Array(WORKER_COUNT) { Worker.start() }
val expectedWorkers = mutableListOf(Worker.current)
(0 until WORKER_COUNT step 2).forEach { i ->
workers[i].requestTermination().result
expectedWorkers.add(workers[i + 1])
}
assertEquals(expectedWorkers.toSet(), Worker.activeWorkers.toSet())
(0 until WORKER_COUNT step 2).forEach { i ->
workers[i + 1].requestTermination().result
}
}
@@ -0,0 +1,429 @@
/*
* 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 test.native.concurrent
import kotlin.concurrent.AtomicInt
import kotlin.native.concurrent.*
import kotlin.test.*
class WorkerTest {
@Test
fun execute() = withWorker {
val future = execute(TransferMode.SAFE, { "Input" }) { "$it processed" }
assertEquals("Input processed", future.result)
}
class MyError : Exception("My error")
@Test
fun executeWithException() = withWorker(errorReporting = false) {
val future = execute(TransferMode.SAFE, {}) { throw MyError() }
// Not `MyError`.
assertFailsWith<IllegalStateException> { future.result }
assertEquals("Still working", execute(TransferMode.SAFE, { "Still" }) { "$it working" }.result)
}
@OptIn(FreezingIsDeprecated::class)
@Test
fun executeWithDetachedObjectGraph() = withWorker {
data class SharedDataMember(val double: Double)
data class SharedData(val string: String, val int: Int, val member: SharedDataMember)
// Here we do rather strange thing. To test object detach API we detach object graph,
// pass detached graph to a worker, where we manually reattached passed value.
val future = execute(TransferMode.SAFE, {
DetachedObjectGraph { SharedData("Hello", 10, SharedDataMember(0.1)) }.asCPointer()
}) {
DetachedObjectGraph<SharedData>(it).attach()
}
assertEquals(SharedData("Hello", 10, SharedDataMember(double = 0.1)), future.result)
}
@Test
fun executeOnWrongThread() {
val worker = Worker.start()
worker.requestTermination().result
val exception = assertFailsWith<IllegalStateException> {
worker.execute(TransferMode.SAFE, {}, {}).result
}
assertEquals("Worker is already terminated", exception.message)
}
@Test
fun waitForMultipleFutures() {
val workers = Array(5) { Worker.start() }
val futures = (1..3).flatMap { attempt ->
workers.mapIndexed { index, worker ->
worker.execute(TransferMode.SAFE, { "$attempt: Input $index" }) { "$it processed" }
}
}
val actual = waitForMultipleFutures(futures, 10000)
val expected = (1..3).flatMap { attempt ->
(0 until 5).map { index -> "$attempt: Input $index processed" }
}.toSet()
// actual cannot be empty.
assertTrue(actual.isNotEmpty())
// Everything in actual must be in expected.
// The reverse is not required to be true: waitForMultipleFutures may return when
// only some futures have completed.
actual.forEach { future ->
// Every actual future is also computed.
assertEquals(FutureState.COMPUTED, future.state)
assertTrue(expected.contains(future.result))
}
workers.forEach {
it.requestTermination().result
}
}
@Test
fun executeWithConcurrentArrayModification() {
val workers = Array(100) { Worker.start() }
val array = Array(workers.size) { it }
val futures = workers.mapIndexed { index, worker ->
worker.execute(TransferMode.SAFE, { array to index }) { (array, index) ->
array[index] += index
}
}
while (waitForMultipleFutures(futures, 10000).size < futures.size) {
}
array.forEachIndexed { index, value ->
assertEquals(index * 2, value)
}
workers.forEach {
it.requestTermination().result
}
}
@Test
fun executeAfter() = withWorker {
val counter = AtomicInt(0)
executeAfter(0) {
assertTrue(Worker.current.park(10_000_000, false))
assertEquals(counter.value, 0)
assertTrue(Worker.current.processQueue())
assertEquals(1, counter.value)
// Let main proceed.
counter.incrementAndGet() // counter becomes 2 here.
assertTrue(Worker.current.park(10_000_000, true))
assertEquals(3, counter.value)
}
executeAfter(0) {
counter.incrementAndGet()
Unit
}
while (counter.value < 2) {
Worker.current.park(1_000)
}
executeAfter(0) {
counter.incrementAndGet()
Unit
}
while (counter.value == 2) {
Worker.current.park(1_000)
}
}
@Test
fun executeAfterNegativeDelay() = withWorker {
assertFailsWith<IllegalArgumentException> { executeAfter(-1) {} }
Unit
}
@Test
fun executeAfterModify() = withWorker {
var v = 1
val done = AtomicInt(0)
executeAfter(0) {
v++
assertEquals(2, v)
done.value = 1
}
while (done.value == 0) {
}
assertEquals(2, v)
}
@Test
fun executeAfterOrdering() = withWorker {
val counter = AtomicInt(0)
val lastTask = AtomicInt(0)
executeAfter(20_000) {
lastTask.value = 1
counter.incrementAndGet()
}
executeAfter(10_000) {
lastTask.value = 2
counter.incrementAndGet()
}
// Wait for both tasks to complete.
while (counter.value != 2) {
}
// Task with id 1 was scheduled to execute later, so it has won.
assertEquals(1, lastTask.value)
}
@Test
fun executeAfterCancelled() {
val worker = Worker.start()
val future = worker.execute(TransferMode.SAFE, {}) {
// Here we processed termination request.
assertEquals(false, Worker.current.processQueue())
}
worker.executeAfter(1_000_000_000L) { error("FAILURE") }
// Request worker to terminate and wait for the request to be processed.
worker.requestTermination(processScheduledJobs = false).result
// Now wait for the worker to complete termination, cleaning up after itself.
waitWorkerTermination(worker)
// `future` is bound to terminated `worker` and so it's not available anymore.
assertFailsWith<IllegalStateException> { future.result }
}
@Test
fun executeAfterOnMain() {
var done = false
Worker.current.executeAfter(0) {
done = true
}
// Not executed immediately.
assertFalse(done)
// The current worker's queue may be filled with other tasks, so we must loop.
while (!done) {
Worker.current.processQueue()
}
}
// This test checks that when multiple `executeAfter` jobs are submitted to `targetWorker` and have the
// same scheduled execution time (in micros since an epoch), nether of them gets lost.
@Test
fun executeAfterScheduledTimeClash() = withWorker {
val targetWorker = this
val mainWorker = Worker.current
// Configuration of the test.
val numberOfSubmitters = 2
val numberOfTasks = 100
val delayInMicroseconds = 100L
val submitters = Array(numberOfSubmitters) { Worker.start() }
try {
val readySubmittersCounter = AtomicInt(0)
val executedTasksCounter = AtomicInt(0)
val finishedBatchesCounter = AtomicInt(0)
submitters.forEach {
it.executeAfter(0L) {
readySubmittersCounter.incrementAndGet()
// Wait for other submitters, to make them all start at the same time:
while (readySubmittersCounter.value != numberOfSubmitters) {
}
// Concurrently submit tasks with matching scheduled execution time:
repeat(numberOfTasks) {
targetWorker.executeAfter(delayInMicroseconds) {
executedTasksCounter.incrementAndGet()
Unit
}
}
// Use larger delay for the task below, to make sure it gets executed after
// the tasks above submitted by the same worker.
// If the order is wrong, the test will fail as well.
// NOTE: the code below was affected by the same problem with clashing times, so despite all the effort
// the test still might hang without a fix.
targetWorker.executeAfter(delayInMicroseconds + 1) {
mainWorker.executeAfter(0L) {
finishedBatchesCounter.incrementAndGet()
Unit
}
}
}
}
while (finishedBatchesCounter.value != numberOfSubmitters) {
// Wait and allow processing the `finishedBatchesCounter.increment()` tasks above:
Worker.current.park(delayInMicroseconds, process = true)
}
// Note: we could have just waited for the condition above to become true,
// but this would mean that the test would hang in case of failure, which is not quite convenient.
assertEquals(numberOfSubmitters * numberOfTasks, executedTasksCounter.value)
} finally {
submitters.forEach { it.requestTermination().result }
}
}
@Test
fun executeAfterOnWrongThread() {
val worker = Worker.start()
worker.requestTermination().result
val exception = assertFailsWith<IllegalStateException> {
worker.executeAfter(0L) {}
}
assertEquals("Worker is already terminated", exception.message)
}
@Test
fun withName() = withWorker(name = "Lumberjack") {
execute(TransferMode.SAFE, {}) {
assertEquals("Lumberjack", Worker.current.name)
}.result
assertEquals("Lumberjack", name)
}
@Test
fun nameOnWrongThread() {
val worker = Worker.start(name = "Lumberjack")
worker.requestTermination().result
val exception = assertFailsWith<IllegalStateException> {
worker.name
}
assertEquals("Worker is already terminated", exception.message)
}
@Test
fun park() = withWorker {
val counter = AtomicInt(0)
val f1 = execute(TransferMode.SAFE, { counter }) { counter ->
Worker.current.park(Long.MAX_VALUE / 1000L, process = true)
counter.incrementAndGet()
}
// wait a bit
Worker.current.park(10_000L)
// submit a task
val f2 = execute(TransferMode.SAFE, { counter }) { counter ->
counter.incrementAndGet()
}
f1.result
f2.result
assertEquals(2, counter.value)
}
@Test
fun parkMain() = withWorker {
val main = Worker.current
val counter = AtomicInt(0)
executeAfter(1000) {
main.executeAfter(1) {
counter.incrementAndGet()
Unit
}
}
assertTrue(main.park(1_000_000_000L, process = true))
assertEquals(1, counter.value)
}
@Test
fun parkOnWrongThread() = withWorker {
val exception = assertFailsWith<IllegalStateException> {
park(1L)
}
assertEquals("Worker is not current or already terminated", exception.message)
}
@Test
fun parkZeroTimeout() {
Worker.current.park(0, process = true)
}
@Test
fun processQueue() = withWorker {
val counter = AtomicInt(0)
val future1 = execute(TransferMode.SAFE, { counter }) { counter ->
assertEquals(0, counter.value)
// Process following request.
while (!Worker.current.processQueue()) {
}
// Ensure it has an effect.
assertEquals(1, counter.value)
// No more non-terminating tasks in this worker queue.
assertEquals(false, Worker.current.processQueue())
}
val future2 = execute(TransferMode.SAFE, { counter }) { counter ->
counter.incrementAndGet()
}
future2.result
future1.result
}
@Test
fun processQueueOnWrongThread() = withWorker {
val exception = assertFailsWith<IllegalStateException> {
processQueue()
}
assertEquals("Worker is not current or already terminated", exception.message)
}
@Test
fun requestTerminationOnWrongThread() {
val worker = Worker.start()
worker.requestTermination().result
val exception = assertFailsWith<IllegalStateException> {
worker.requestTermination()
}
assertEquals("Worker is already terminated", exception.message)
}
@Test
fun activeWorkers() {
val workers = Array(10) { Worker.start() }
val actualWorkers = Worker.activeWorkers.toSet()
assertTrue(actualWorkers.size - workers.size == 1 || actualWorkers.size - workers.size == 2,
"actualWorkers.size = ${actualWorkers.size} workers.size = ${workers.size} actual size must be greater by 1 (main worker) or 2 (cleaners worker)")
workers.forEach {
actualWorkers.contains(it)
}
actualWorkers.contains(Worker.current)
val terminatedWorkers = mutableSetOf<Worker>()
(workers.indices step 2).forEach {
val worker = workers[it]
worker.requestTermination().result
terminatedWorkers.add(worker)
}
val actualWorkersAfterTermination = Worker.activeWorkers.toSet()
assertEquals(terminatedWorkers, actualWorkers - actualWorkersAfterTermination)
(workers.indices step 2).forEach {
workers[it + 1].requestTermination().result
}
}
@Test
fun futureConsumedTwice(): Unit = withWorker {
val future = execute(TransferMode.SAFE, {}) {
42
}
assertEquals(42, future.result)
val exception = assertFailsWith<IllegalStateException> {
future.result
}
assertEquals("Future is in an invalid state", exception.message)
}
}
@@ -0,0 +1,112 @@
/*
* 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 test.utils
import kotlin.concurrent.AtomicInt
import kotlin.native.concurrent.*
import kotlin.test.*
abstract class AbstractExplicitModeLazyTest {
abstract val mode: LazyThreadSafetyMode
@Test
fun finiteRecursion() {
var y = 20
class C {
val finiteRecursion: Int by lazy(mode) {
if (y < 17) 42 else {
y -= 1
finiteRecursion + 1
}
}
}
assertEquals(if (mode == LazyThreadSafetyMode.SYNCHRONIZED) 46 else 42, C().finiteRecursion)
}
@Test
fun captureThis() {
class C {
val self by lazy(mode) { this }
}
val self = C()
assertEquals(self, self.self)
}
@Test
fun throwException() {
class C {
val thrower by lazy<String>(mode) {
error("failure")
}
}
val self = C()
repeat(10) {
assertFailsWith<IllegalStateException> {
self.thrower
}
}
}
@Test
fun multiThreadedInit() {
val initializerCallCount = AtomicInt(0)
class C {
val data by lazy(mode) {
initializerCallCount.getAndIncrement()
Any()
}
}
val self = C()
val workers = Array(20) { Worker.start() }
val initialized = AtomicInt(0)
val canStart = AtomicInt(0)
val futures = workers.map {
it.execute(TransferMode.SAFE, { Triple(initialized, canStart, self) }) { (initialized, canStart, self) ->
initialized.incrementAndGet()
while (canStart.value != 1) {
}
self.data
}
}
while (initialized.value < workers.size) {
}
canStart.value = 1
val results = mutableSetOf<Any>()
futures.forEach {
results += it.result
}
if (mode == LazyThreadSafetyMode.SYNCHRONIZED) {
assertEquals(1, initializerCallCount.value)
} else {
assertTrue(initializerCallCount.value > 0)
assertTrue(initializerCallCount.value <= workers.size)
}
assertSame(self.data, results.single())
workers.forEach {
it.requestTermination().result
}
}
}
class SynchronizedExplicitModeLazyTest : AbstractExplicitModeLazyTest() {
override val mode: LazyThreadSafetyMode
get() = LazyThreadSafetyMode.SYNCHRONIZED
}
class PublicationExplicitModeLazyTest : AbstractExplicitModeLazyTest() {
override val mode: LazyThreadSafetyMode
get() = LazyThreadSafetyMode.PUBLICATION
}
+1
View File
@@ -547,6 +547,7 @@ kotlin {
optIn("kotlin.native.runtime.NativeRuntimeApi")
optIn("kotlin.native.internal.InternalForKotlinNative")
optIn("kotlinx.cinterop.ExperimentalForeignApi")
optIn("kotlin.native.concurrent.ObsoleteWorkersApi")
}
}
}
@@ -27,7 +27,7 @@ import org.jetbrains.kotlin.konan.test.blackbox.support.group.PredefinedTestCase
freeCompilerArgs = [
ENABLE_MPP, STDLIB_IS_A_FRIEND, ENABLE_X_STDLIB_API, ENABLE_X_ENCODING_API, ENABLE_RANGE_UNTIL,
ENABLE_X_FOREIGN_API, ENABLE_X_NATIVE_API, ENABLE_OBSOLETE_NATIVE_API, ENABLE_NATIVE_RUNTIME_API,
ENABLE_INTERNAL_FOR_KOTLIN_NATIVE],
ENABLE_OBSOLETE_WORKERS_API, ENABLE_INTERNAL_FOR_KOTLIN_NATIVE],
sourceLocations = [
"libraries/stdlib/test/**.kt",
"libraries/stdlib/common/test/**.kt",
@@ -53,7 +53,7 @@ class StdlibTest : AbstractNativeBlackBoxTest() {
freeCompilerArgs = [
ENABLE_MPP, STDLIB_IS_A_FRIEND, ENABLE_X_STDLIB_API, ENABLE_X_ENCODING_API, ENABLE_RANGE_UNTIL,
ENABLE_X_FOREIGN_API, ENABLE_X_NATIVE_API, ENABLE_OBSOLETE_NATIVE_API, ENABLE_NATIVE_RUNTIME_API,
ENABLE_INTERNAL_FOR_KOTLIN_NATIVE,
ENABLE_OBSOLETE_WORKERS_API, ENABLE_INTERNAL_FOR_KOTLIN_NATIVE,
"-Xcommon-sources=libraries/stdlib/common/test/jsCollectionFactories.kt",
"-Xcommon-sources=libraries/stdlib/common/test/testUtils.kt",
"-Xcommon-sources=libraries/stdlib/test/testUtils.kt",
@@ -84,6 +84,7 @@ private const val ENABLE_X_FOREIGN_API = "-opt-in=kotlinx.cinterop.ExperimentalF
private const val ENABLE_X_NATIVE_API = "-opt-in=kotlin.experimental.ExperimentalNativeApi"
private const val ENABLE_OBSOLETE_NATIVE_API = "-opt-in=kotlin.native.ObsoleteNativeApi"
private const val ENABLE_NATIVE_RUNTIME_API = "-opt-in=kotlin.native.runtime.NativeRuntimeApi"
private const val ENABLE_OBSOLETE_WORKERS_API = "-opt-in=kotlin.native.concurrent.ObsoleteWorkersApi"
private const val ENABLE_INTERNAL_FOR_KOTLIN_NATIVE = "-opt-in=kotlin.native.internal.InternalForKotlinNative"
private const val ENABLE_RANGE_UNTIL = "-XXLanguage:+RangeUntilOperator" // keep until 1.8
private const val DISABLED_STDLIB_TEST = "test.collections.CollectionTest.abstractCollectionToArray"