Skip to content

Instantly share code, notes, and snippets.

@libliboom
Created January 3, 2021 16:15
Show Gist options
  • Save libliboom/3efb24f4766e51563e198f649fe96015 to your computer and use it in GitHub Desktop.
Save libliboom/3efb24f4766e51563e198f649fe96015 to your computer and use it in GitHub Desktop.
import junit.framework.TestCase.assertEquals
import kotlinx.coroutines.*
import kotlinx.coroutines.test.TestCoroutineDispatcher
import org.junit.Rule
import org.junit.Test
/**
* Check the official example for ScopeBuilder with the following url.
* https://kotlinlang.org/docs/reference/coroutines/basics.html#scope-builder
* **/
@ExperimentalStdlibApi
@ExperimentalCoroutinesApi
class CoroutinesExecutionOrderTest {
/**
* By default, [TestCoroutineDispatcher] is immediate.
* That means any tasks scheduled to be run without delay are immediately executed.
*/
@get: Rule
val mainCoroutineScopeRule: MainCoroutineScopeRule = MainCoroutineScopeRule()
@get: Rule
var retry: RetryRule = RetryRule(100)
@Test
fun `official example for ScopeBuilder`() {
val result = mutableListOf<String>()
// Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion.
runBlocking { /** blocks the current thread for waiting **/
launch {
println("1-${coroutineContext[CoroutineDispatcher]}")
result.add("1")
}
// This function returns as soon as the given block and all its children coroutines are completed.
coroutineScope { /** just suspends **/
launch {
println("2-${coroutineContext[CoroutineDispatcher]}")
result.add("2")
}
println("3-${coroutineContext[CoroutineDispatcher]}")
result.add("3")
}
println("4-${coroutineContext[CoroutineDispatcher]}")
result.add("4")
}
assertEquals(listOf("3", "1", "2", "4"), result)
}
@Test
fun `change coroutineScope to runBlocking`() {
val result = mutableListOf<String>()
// Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion.
runBlocking { /** blocks the current thread for waiting **/
launch {
println("1-${coroutineContext[CoroutineDispatcher]}")
result.add("1")
}
runBlocking {
launch {
println("2-${coroutineContext[CoroutineDispatcher]}")
result.add("2")
}
println("3-${coroutineContext[CoroutineDispatcher]}")
result.add("3")
}
println("4-${coroutineContext[CoroutineDispatcher]}")
result.add("4")
}
assertEquals(listOf("1", "3", "2", "4"), result)
}
@Test
fun `change coroutineScope to async or launch`() {
val result = mutableListOf<String>()
// Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion.
runBlocking { /** blocks the current thread for waiting **/
launch {
println("1-${coroutineContext[CoroutineDispatcher]}")
result.add("1")
}
async {
launch {
println("2-${coroutineContext[CoroutineDispatcher]}")
result.add("2")
}
println("3-${coroutineContext[CoroutineDispatcher]}")
result.add("3")
}
println("4-${coroutineContext[CoroutineDispatcher]}")
result.add("4")
}
assertEquals(listOf("4", "1", "3", "2"), result)
}
@Test
fun `change coroutineScope to GlobalScope's launch`() {
val result = mutableListOf<String>()
// Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion.
runBlocking { /** blocks the current thread for waiting **/
launch {
println("1-${coroutineContext[CoroutineDispatcher]}")
result.add("1")
}
GlobalScope.launch {
launch {
println("2-${coroutineContext[CoroutineDispatcher]}")
result.add("2")
}
println("3-${coroutineContext[CoroutineDispatcher]}")
result.add("3")
}
println("4-${coroutineContext[CoroutineDispatcher]}")
result.add("4")
}
// It's depends on Event loop. That means there is no guarantees on the order of execution
}
@Test
fun `change coroutineScope to runBlocking with other Dispatcher`() {
val result = mutableListOf<String>()
// Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion.
runBlocking { /** blocks the current thread for waiting **/
launch {
println("1-${coroutineContext[CoroutineDispatcher]}")
result.add("1")
}
runBlocking(Dispatchers.IO) {
launch {
println("2-${coroutineContext[CoroutineDispatcher]}")
result.add("2")
}
println("3-${coroutineContext[CoroutineDispatcher]}")
result.add("3")
}
println("4-${coroutineContext[CoroutineDispatcher]}")
result.add("4")
}
// It's depends on Event loop. That means there is no guarantees on the order of execution
}
@Test
fun `change coroutineScope to runBlocking with nested one`() {
val result = mutableListOf<String>()
// Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion.
runBlocking { /** blocks the current thread for waiting **/
launch {
println("1-${coroutineContext[CoroutineDispatcher]}")
result.add("1")
}
runBlocking {
runBlocking { /** blocks the current thread for waiting **/
launch {
println("2-${coroutineContext[CoroutineDispatcher]}")
result.add("2")
}
runBlocking {
launch {
println("3-${coroutineContext[CoroutineDispatcher]}")
result.add("3")
}
println("4-${coroutineContext[CoroutineDispatcher]}")
result.add("4")
}
println("5-${coroutineContext[CoroutineDispatcher]}")
result.add("5")
}
}
println("6-${coroutineContext[CoroutineDispatcher]}")
result.add("6")
}
assertEquals(listOf("1", "2", "4", "3", "5", "6"), result)
}
@Test
fun `part of change coroutineScope to runBlocking with nested one`() {
val result = mutableListOf<String>()
runBlocking {
launch {
println("3-${coroutineContext[CoroutineDispatcher]}")
result.add("3")
}
println("4-${coroutineContext[CoroutineDispatcher]}")
result.add("4")
}
assertEquals(listOf("4", "3"), result)
}
}
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.TestCoroutineScope
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.junit.rules.TestWatcher
import org.junit.runner.Description
@ExperimentalCoroutinesApi
open class MainCoroutineScopeRule(
val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
) : TestWatcher(),
TestCoroutineScope by TestCoroutineScope(testDispatcher) {
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
cleanupTestCoroutines()
Dispatchers.resetMain()
}
}
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
// https://stackoverflow.com/questions/8295100/how-to-re-run-failed-junit-tests-immediately/8301639#8301639
class RetryRule(private val retryCount: Int) : TestRule {
override fun apply(
base: Statement,
description: Description
): Statement {
return object : Statement() {
@Throws(Throwable::class)
override fun evaluate() {
var caughtThrowable: Throwable? = null
var failuresCount = 0
for (i in 0 until retryCount) {
try {
base.evaluate()
} catch (t: Throwable) {
caughtThrowable = t
System.err.println(
description.displayName
.toString() + ": run " + (i + 1) + " failed:"
)
t.printStackTrace()
++failuresCount
}
}
if (caughtThrowable == null) return
throw AssertionError(
(description.displayName
.toString() + ": failures " + failuresCount + " out of "
+ retryCount + " tries. See last throwable as the cause."), caughtThrowable
)
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment