1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 import androidx.lifecycle.MainDispatcherChecker
18 import java.util.concurrent.ExecutorService
19 import java.util.concurrent.Executors
20 import kotlin.coroutines.CoroutineContext
21 import kotlin.test.Test
22 import kotlin.test.assertFalse
23 import kotlin.test.assertTrue
24 import kotlinx.coroutines.Dispatchers
25 import kotlinx.coroutines.ExperimentalCoroutinesApi
26 import kotlinx.coroutines.MainCoroutineDispatcher
27 import kotlinx.coroutines.Runnable
28 import kotlinx.coroutines.runBlocking
29 import kotlinx.coroutines.test.resetMain
30 import kotlinx.coroutines.test.setMain
31 
32 class MainDispatcherCheckerTest {
33     @Test
checkMainDispatchernull34     fun checkMainDispatcher() {
35         runBlocking(Dispatchers.Main) { assertTrue(MainDispatcherChecker.isMainDispatcherThread()) }
36         runBlocking(Dispatchers.Main.immediate) {
37             assertTrue(MainDispatcherChecker.isMainDispatcherThread())
38         }
39     }
40 
41     @Test
checkNonMainDispatchernull42     fun checkNonMainDispatcher() {
43         runBlocking(Dispatchers.IO) { assertFalse(MainDispatcherChecker.isMainDispatcherThread()) }
44     }
45 
46     @OptIn(ExperimentalCoroutinesApi::class)
47     @Test
checkMainDispatcherChangednull48     fun checkMainDispatcherChanged() {
49         try {
50             Dispatchers.setMain(ThreadChangingMainDispatcher)
51             runBlocking(Dispatchers.Main) {
52                 assertTrue(MainDispatcherChecker.isMainDispatcherThread())
53             }
54             ThreadChangingMainDispatcher.changeThread()
55             runBlocking(Dispatchers.Main) {
56                 assertTrue(MainDispatcherChecker.isMainDispatcherThread())
57             }
58         } finally {
59             Dispatchers.resetMain()
60         }
61     }
62 
63     private object ThreadChangingMainDispatcher : MainCoroutineDispatcher() {
64         private var thread: Thread? = null
65         private var executor = newExecutorService()
66 
67         override val immediate: MainCoroutineDispatcher
68             get() = this
69 
dispatchnull70         override fun dispatch(context: CoroutineContext, block: Runnable) {
71             // support reentrancy
72             if (Thread.currentThread() == thread) {
73                 block.run()
74             } else {
75                 executor.submit(block)
76             }
77         }
78 
changeThreadnull79         fun changeThread() {
80             executor.shutdown()
81             executor = newExecutorService()
82         }
83 
newExecutorServicenull84         private fun newExecutorService(): ExecutorService =
85             Executors.newSingleThreadExecutor {
86                 thread = Thread(it)
87                 thread
88             }
89     }
90 }
91