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