1 /*
2  * Copyright (C) 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 package com.android.systemui.flags
17 
18 import androidx.test.filters.SmallTest
19 import com.android.systemui.SysuiTestCase
20 import com.android.systemui.util.mockito.any
21 import kotlinx.coroutines.test.StandardTestDispatcher
22 import kotlinx.coroutines.test.TestScope
23 import kotlinx.coroutines.test.advanceUntilIdle
24 import kotlinx.coroutines.test.runTest
25 import org.junit.Before
26 import org.junit.Test
27 import org.mockito.Mock
28 import org.mockito.Mockito.never
29 import org.mockito.Mockito.verify
30 import org.mockito.MockitoAnnotations
31 
32 /**
33  * Be careful with the {FeatureFlagsReleaseRestarter} in this test. It has a call to System.exit()!
34  */
35 @SmallTest
36 class ConditionalRestarterTest : SysuiTestCase() {
37     private lateinit var restarter: ConditionalRestarter
38 
39     @Mock private lateinit var systemExitRestarter: SystemExitRestarter
40 
41     val restartDelayMs = 0L
42     val dispatcher = StandardTestDispatcher()
43     val testScope = TestScope(dispatcher)
44 
45     val conditionA = FakeCondition()
46     val conditionB = FakeCondition()
47 
48     @Before
49     fun setup() {
50         MockitoAnnotations.initMocks(this)
51         restarter =
52             ConditionalRestarter(
53                 systemExitRestarter,
54                 setOf(conditionA, conditionB),
55                 restartDelayMs,
56                 testScope,
57                 dispatcher
58             )
59     }
60 
61     @Test
62     fun restart_ImmediatelySatisfied() =
63         testScope.runTest {
64             conditionA.canRestart = true
65             conditionB.canRestart = true
66             restarter.restartSystemUI("Restart for test")
67             advanceUntilIdle()
68             verify(systemExitRestarter).restartSystemUI(any())
69         }
70 
71     @Test
72     fun restart_WaitsForConditionA() =
73         testScope.runTest {
74             conditionA.canRestart = false
75             conditionB.canRestart = true
76 
77             restarter.restartSystemUI("Restart for test")
78             advanceUntilIdle()
79             // No restart occurs yet.
80             verify(systemExitRestarter, never()).restartSystemUI(any())
81 
82             conditionA.canRestart = true
83             conditionA.retryFn?.invoke()
84             advanceUntilIdle()
85             verify(systemExitRestarter).restartSystemUI(any())
86         }
87 
88     @Test
89     fun restart_WaitsForConditionB() =
90         testScope.runTest {
91             conditionA.canRestart = true
92             conditionB.canRestart = false
93 
94             restarter.restartSystemUI("Restart for test")
95             advanceUntilIdle()
96             // No restart occurs yet.
97             verify(systemExitRestarter, never()).restartSystemUI(any())
98 
99             conditionB.canRestart = true
100             conditionB.retryFn?.invoke()
101             advanceUntilIdle()
102             verify(systemExitRestarter).restartSystemUI(any())
103         }
104 
105     @Test
106     fun restart_WaitsForAllConditions() =
107         testScope.runTest {
108             conditionA.canRestart = true
109             conditionB.canRestart = false
110 
111             restarter.restartSystemUI("Restart for test")
112             advanceUntilIdle()
113             // No restart occurs yet.
114             verify(systemExitRestarter, never()).restartSystemUI(any())
115 
116             // B becomes true, but A is now false
117             conditionA.canRestart = false
118             conditionB.canRestart = true
119             conditionB.retryFn?.invoke()
120             advanceUntilIdle()
121             // No restart occurs yet.
122             verify(systemExitRestarter, never()).restartSystemUI(any())
123 
124             conditionA.canRestart = true
125             conditionA.retryFn?.invoke()
126             advanceUntilIdle()
127             verify(systemExitRestarter).restartSystemUI(any())
128         }
129 
130     class FakeCondition : ConditionalRestarter.Condition {
131         var retryFn: (() -> Unit)? = null
132         var canRestart = false
133 
134         override fun canRestartNow(retryFn: () -> Unit): Boolean {
135             this.retryFn = retryFn
136 
137             return canRestart
138         }
139     }
140 }
141