1 /*
2  * Copyright 2023 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 package androidx.compose.ui.test
18 
19 import kotlin.math.ceil
20 import kotlinx.coroutines.ExperimentalCoroutinesApi
21 import kotlinx.coroutines.test.TestCoroutineScheduler
22 
23 @OptIn(ExperimentalCoroutinesApi::class)
24 internal abstract class AbstractMainTestClock(
25     private val testScheduler: TestCoroutineScheduler,
26     private val frameDelayMillis: Long,
27     private val runOnUiThread: (action: () -> Unit) -> Unit
28 ) : MainTestClock {
29 
30     override val currentTime: Long
31         get() = testScheduler.currentTime
32 
33     override var autoAdvance: Boolean = true
34 
advanceTimeByFramenull35     override fun advanceTimeByFrame() {
36         advanceScheduler(frameDelayMillis)
37     }
38 
advanceTimeBynull39     override fun advanceTimeBy(milliseconds: Long, ignoreFrameDuration: Boolean) {
40         val actualDelay =
41             if (ignoreFrameDuration) {
42                 milliseconds
43             } else {
44                 ceil(milliseconds.toDouble() / frameDelayMillis).toLong() * frameDelayMillis
45             }
46         advanceScheduler(actualDelay)
47     }
48 
advanceTimeUntilnull49     override fun advanceTimeUntil(timeoutMillis: Long, condition: () -> Boolean) {
50         val startTime = currentTime
51         runOnUiThread {
52             while (!condition()) {
53                 advanceScheduler(frameDelayMillis)
54                 if (currentTime - startTime > timeoutMillis) {
55                     throw ComposeTimeoutException(
56                         "Condition still not satisfied after $timeoutMillis ms"
57                     )
58                 }
59             }
60         }
61     }
62 
advanceSchedulernull63     private fun advanceScheduler(millis: Long) {
64         runOnUiThread {
65             // advanceTimeBy() runs all tasks up to, but not including, the new time
66             testScheduler.advanceTimeBy(millis)
67             // So finish with a call to runCurrent() to run all tasks at the new time
68             testScheduler.runCurrent()
69         }
70     }
71 }
72