From af036805ec389be52c4ce20a3f46b6697a6b9427 Mon Sep 17 00:00:00 2001 From: Jerry Lee Date: Mon, 13 Feb 2023 18:50:40 +0800 Subject: [PATCH] test: add `CompletableFutureUseTest.kt` --- .../cffu/CompletableFutureUseTest.kt | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 src/test/java/io/foldright/cffu/CompletableFutureUseTest.kt diff --git a/src/test/java/io/foldright/cffu/CompletableFutureUseTest.kt b/src/test/java/io/foldright/cffu/CompletableFutureUseTest.kt new file mode 100644 index 00000000..c0d4a0a6 --- /dev/null +++ b/src/test/java/io/foldright/cffu/CompletableFutureUseTest.kt @@ -0,0 +1,74 @@ +package io.foldright.cffu + +import io.kotest.core.spec.style.AnnotationSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe +import io.kotest.matchers.string.shouldNotStartWith +import io.kotest.matchers.string.shouldStartWith +import java.util.concurrent.* +import java.util.concurrent.atomic.AtomicLong +import kotlin.random.Random +import kotlin.random.nextULong + +class CompletableFutureUseTest : AnnotationSpec() { + /** + * - `Async` methods(e.g. `runAsync`/`thenRunAsync`, etc.) can set `executor`; + * if executor argument is absent use default executor of [CompletableFuture.ASYNC_POOL] (normally is a [ForkJoinPool]). + * - non-`Async` methods use the executor of previous [CompletableFuture]. + */ + @Test + fun executor_inheritance_behavior() { + val testThread = Thread.currentThread() + CompletableFuture + .runAsync { + Thread.currentThread() shouldNotBe testThread + + Thread.currentThread().name shouldNotStartWith threadNamePrefix + } + .thenRunAsync({ + Thread.currentThread().name shouldStartWith threadNamePrefix + }, executor) // !! switched executor !! + .thenRun { + // when run NOT async, + // executor is INHERITED after switch. + Thread.currentThread().name shouldStartWith threadNamePrefix + } + .thenRunAsync { + // when run ASYNC, + // + // - executor is NOT inherited after switch!! + // - use the DEFAULT EXECUTOR of CompletableFuture, if no executor specified. + Thread.currentThread().name shouldNotStartWith threadNamePrefix + } + .join() + } + + //////////////////////////////////////////////////////////////////////////////// + // executor field + //////////////////////////////////////////////////////////////////////////////// + + private lateinit var executor: ExecutorService + + private val threadNamePrefix = "CompletableFutureUseTest_${Random.nextULong()}-" + + @BeforeAll + fun beforeAll() { + val threadFactory = ThreadFactory { r -> + val counter = AtomicLong() + Thread(r).apply { + name = "$threadNamePrefix${counter.getAndIncrement()}" + isDaemon = true + } + } + executor = ThreadPoolExecutor( + 4, 4, 1, TimeUnit.MINUTES, + ArrayBlockingQueue(10), threadFactory + ) + } + + @AfterAll + fun afterAll() { + executor.shutdown() + executor.awaitTermination(1, TimeUnit.SECONDS) shouldBe true + } +}