• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 com.android.safetycenter.testing
18 
19 import android.util.Log
20 import java.time.Duration
21 import kotlinx.coroutines.DEBUG_PROPERTY_NAME
22 import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_AUTO
23 import kotlinx.coroutines.DEBUG_PROPERTY_VALUE_ON
24 import kotlinx.coroutines.delay
25 import kotlinx.coroutines.runBlocking
26 import kotlinx.coroutines.withTimeout
27 import kotlinx.coroutines.withTimeoutOrNull
28 
29 /** A class that facilitates interacting with coroutines. */
30 object Coroutines {
31 
32     /** A long timeout, to be used for actions that are expected to complete. */
33     val TIMEOUT_LONG: Duration = Duration.ofSeconds(25)
34 
35     /** A short timeout, to be used for actions that are expected not to complete. */
36     val TIMEOUT_SHORT: Duration = Duration.ofSeconds(1)
37 
38     /** Shorthand for [runBlocking] combined with [withTimeout]. */
runBlockingWithTimeoutnull39     fun <T> runBlockingWithTimeout(timeout: Duration = TIMEOUT_LONG, block: suspend () -> T): T =
40         runBlocking {
41             withTimeout(timeout.toMillis()) { block() }
42         }
43 
44     /** Shorthand for [runBlocking] combined with [withTimeoutOrNull] */
runBlockingWithTimeoutOrNullnull45     fun <T> runBlockingWithTimeoutOrNull(
46         timeout: Duration = TIMEOUT_LONG,
47         block: suspend () -> T
48     ): T? = runBlocking { withTimeoutOrNull(timeout.toMillis()) { block() } }
49 
50     /** Check a condition using coroutines with a timeout. */
waitForWithTimeoutnull51     fun waitForWithTimeout(
52         timeout: Duration = TIMEOUT_LONG,
53         checkPeriod: Duration = CHECK_PERIOD,
54         condition: () -> Boolean
55     ) {
56         runBlockingWithTimeout(timeout) { waitFor(checkPeriod, condition) }
57     }
58 
59     /** Retries a [fallibleAction] until no errors are thrown or a timeout occurs. */
waitForSuccessWithTimeoutnull60     fun waitForSuccessWithTimeout(
61         timeout: Duration = TIMEOUT_LONG,
62         checkPeriod: Duration = CHECK_PERIOD,
63         fallibleAction: () -> Unit
64     ) {
65         waitForWithTimeout(timeout, checkPeriod) {
66             try {
67                 fallibleAction()
68                 true
69             } catch (ex: Throwable) {
70                 Log.w(TAG, "Encountered failure, retrying until timeout: $ex")
71                 false
72             }
73         }
74     }
75 
76     /**
77      * Enables debug mode for coroutines, in particular this enables stack traces in case of
78      * failures.
79      */
enableDebuggingnull80     fun enableDebugging() {
81         System.setProperty(DEBUG_PROPERTY_NAME, DEBUG_PROPERTY_VALUE_ON)
82     }
83 
84     /** Resets the debug mode to its original state. */
resetDebuggingnull85     fun resetDebugging() {
86         System.setProperty(DEBUG_PROPERTY_NAME, debugMode)
87     }
88 
89     /** Check a condition using coroutines. */
waitFornull90     private suspend fun waitFor(checkPeriod: Duration = CHECK_PERIOD, condition: () -> Boolean) {
91         while (!condition()) {
92             delay(checkPeriod.toMillis())
93         }
94     }
95 
96     private const val TAG: String = "Coroutines"
97 
98     /** A medium period, to be used for conditions that are expected to change. */
99     private val CHECK_PERIOD: Duration = Duration.ofMillis(250)
100 
101     private val debugMode: String? =
102         System.getProperty(DEBUG_PROPERTY_NAME, DEBUG_PROPERTY_VALUE_AUTO)
103 }
104