Skip to content

Instantly share code, notes, and snippets.

@SardarkhanStella
Last active September 28, 2022 13:57
Show Gist options
  • Save SardarkhanStella/def37394d57baa6734749d00903e5fe0 to your computer and use it in GitHub Desktop.
Save SardarkhanStella/def37394d57baa6734749d00903e5fe0 to your computer and use it in GitHub Desktop.
Coroutine Testing with different Builder, Scope and Cancellation.
import android.util.Log
import kotlinx.coroutines.*
import org.junit.Test
import java.io.IOException
import kotlin.math.pow
class AsyncCoroutineBuilderDemoTest {
@Test
fun correctUseOfCoroutinesVariant1() = runBlocking {
withContext(Dispatchers.Default) {
val deferreds = mutableListOf<Deferred<Int>>()
for (duration in 1..5) {
deferreds.add(
async {
val startTimeNano = System.nanoTime()
var iterations = 0
while (System.nanoTime() < startTimeNano + (duration * 10f.pow(9))) {
iterations++
}
iterations
}
)
}
var totalIterations = 0
for (deferred in deferreds) {
totalIterations += deferred.await()
}
println("total iterations: $totalIterations")
}
}
@Test
fun correctUseOfCoroutinesVariant2() = runBlocking {
withContext(Dispatchers.Default) {
val totalIterations = (1..5).toList().map { duration ->
async {
val startTimeNano = System.nanoTime()
var iterations = 0
while (System.nanoTime() < startTimeNano + (duration * 10f.pow(9))) {
iterations++
}
iterations
}
}.awaitAll().sum()
println("total iterations: $totalIterations")
}
}
@Test
fun main() = runBlocking {
val asyncJob = GlobalScope.launch {
println("1. Exception created via launch coroutine")
// Will be printed to the console by
// Thread.defaultUncaughtExceptionHandler
throw IndexOutOfBoundsException()
}
try {
asyncJob.invokeOnCompletion { exception->
println("exception. $exception")
}
asyncJob.join()
println("2. Joined failed job")
}catch (e:Exception){
println("1.1 Exception caught ${e.message}")
}
val deferred = GlobalScope.async {
println("3. Exception created via async coroutine")
// Nothing is printed, relying on user to call await
throw ArithmeticException()
}
try {
deferred.await()
println("4. Unreachable, this statement is never executed")
} catch (e: Exception) {
println("5. Caught ${e.javaClass.simpleName}")
}
}
@Test
fun mainFunc() {
runBlocking {
// 1
val exceptionHandler = CoroutineExceptionHandler { _,
exception ->
println("Caught $exception")
}
// 2
val job = GlobalScope.launch(exceptionHandler) {
throw AssertionError("My Custom Assertion Error!")
}
// 3
val deferred = GlobalScope.async(exceptionHandler) {
// Nothing will be printed,
// relying on user to call deferred.await()
throw ArithmeticException()
}
// 4
// This suspends current coroutine until all given jobs are complete.
joinAll(job, deferred)
}
}
@Test
fun throwExceptionWhenDefferedCalled() {
runBlocking {
// Set this to 'true' to call await on the deferred variable
val callAwaitOnDeferred = true
val deferred = GlobalScope.async {
// This statement will be printed with or without
// a call to await()
println("Throwing exception from async")
throw ArithmeticException("Something Crashed")
// Nothing is printed, relying on a call to await()
}
if (callAwaitOnDeferred) {
try {
deferred.await()
} catch (e: ArithmeticException) {
println("Caught ArithmeticException")
}
}
}
}
@Test
fun childExceptionHandling() = runBlocking {
// Global Exception Handler
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception with suppressed${exception.suppressed?.contentToString()}")
}
// Parent Job
val parentJob = GlobalScope.launch(handler) {
// Child Job 1
launch {
try {
delay(Long.MAX_VALUE)
} catch (e: Exception) {
println("${e.javaClass.simpleName} in Child Job 1")
} finally {
println("Finally")
throw ArithmeticException()
}
}
// Child Job 3
launch {
try {
delay(Long.MAX_VALUE)
} catch (e: Exception) {
println("${e.javaClass.simpleName} in Child Job 3")
} finally {
println("Finally")
throw ClassNotFoundException()
}
}
// Child Job 2
launch {
println("Job2 Breaks")
delay(100)
throw IllegalStateException()
}
// Delaying the parentJob
delay(Long.MAX_VALUE)
}
// Wait until parentJob completes
parentJob.join()
}
@Test
fun childWithSupervisorScope() = runBlocking {
// 1
val supervisor = SupervisorJob()
with(CoroutineScope(coroutineContext + supervisor)) {
// 3
val topChild = launch {
try {
delay(5000)
} catch (e: CancellationException) {
println("Third child cancelled because supervisor got cancelled.")
}
}
// 2
val firstChild = launch {
println("First child throwing an exception")
try {
throw ArithmeticException()
}catch (e:Exception){
println("exception first child ${e.message}")
}
}
// 3
val secondChild = launch {
println("First child is cancelled: ${firstChild.isCancelled}")
try {
delay(5000)
} catch (e: CancellationException) {
println("Second child cancelled because supervisor got cancelled.")
}
}
// 4
println("Second child is active: ${secondChild.isActive}")
firstChild.join()
supervisor.cancel()
println("Join all.")
secondChild.join()
topChild.join()
}
}
@Test
fun cancelableException() = runBlocking {
// 1
val handler = CoroutineExceptionHandler { _, exception ->
// 6
println("Caught original $exception")
}
// 2
val parentJob = GlobalScope.launch(handler) {
val childJob = launch {
// 4
throw IOException()
}
try {
childJob.join()
} catch (e: CancellationException) {
// 5
println("Rethrowing CancellationException with original cause: ${e.cause}")
throw e
}
}
// 3
parentJob.join()
}
@Test
fun joinCoroutineExample() = runBlocking {
val job = launch {
println("Crunching numbers [Beep.Boop.Beep]...")
delay(1000L)
}
// waits for job's completion
job.join()
println("main: Now I can quit.")
}
@Test
fun joinAllCalls() = runBlocking {
val jobOne = launch(Dispatchers.Default) {
println("Job 1: Crunching numbers [Beep.Boop.Beep]...")
delay(5000L)
}
val jobTwo = async(Dispatchers.Default) {
println("Job 2: Crunching numbers [Beep.Boop.Beep]...")
delay(2000L)
}
// waits for both the jobs to complete
joinAll(jobOne, jobTwo)
println("main: Now I can quit.")
}
@Test
fun cancelAndJoin() = runBlocking {
coroutineScope {
val job = launch {
repeat(1000) { i ->
println("$i. Crunching numbers [Beep.Boop.Beep]…")
delay(500L)
}
}
delay(1300L) // delay a bit
println("main: I am tired of waiting!")
// cancels the job and waits for job’s completion
job.cancelAndJoin()
println("main: Now I can quit.")
}
}
@Test
fun cancelChildren() = runBlocking {
val parentJob = launch {
val childOne = launch {
repeat(1000) { i ->
println("Child Coroutine 1: " +
"$i. Crunching numbers [Beep.Boop.Beep]…")
delay(500L)
}
}
// Handle the exception thrown from `launch`
// coroutine builder
childOne.invokeOnCompletion { exception ->
println("Child One: ${exception?.message}")
}
val childTwo = launch {
repeat(1000) { i ->
println("Child Coroutine 2: " +
"$i. Crunching numbers [Beep.Boop.Beep]…")
delay(500L)
}
}
// Handle the exception thrown from `launch`
// coroutine builder
childTwo.invokeOnCompletion { exception ->
println("Child Two: ${exception?.message}")
}
}
delay(1200L)
println("Calling cancelChildren() on the parentJob")
parentJob.cancelChildren()
println("parentJob isActive: ${parentJob.isActive}")
}
@Test
fun timeoutCancellationTryCatch() = runBlocking {
try {
withTimeout(1500L) {
repeat(1000) { i ->
println("$i. Crunching numbers [Beep.Boop.Beep]...")
delay(500L)
}
}
} catch (e: TimeoutCancellationException) {
println("Caught ${e.javaClass.simpleName}")
}
}
@Test
fun timeoutCancellation() = runBlocking {
withTimeout(1500L) {
repeat(1000) { i ->
println("$i. Crunching numbers [Beep.Boop.Beep]...")
delay(500L)
}
}
}
@Test
fun withTimeoutOrNull() = runBlocking {
val result = withTimeoutOrNull(1300L) {
repeat(1000) { i ->
println("$i. Crunching numbers [Beep.Boop.Beep]...")
delay(500L)
}
"Done" // will get canceled before it produces this result
}
// Result will be `null`
println("Result is $result")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment