• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.server.pm.test.install
18 
19 import com.android.server.pm.utils.RequestThrottle
20 import com.android.server.testutils.TestHandler
21 import com.google.common.collect.Range
22 import com.google.common.truth.LongSubject
23 import com.google.common.truth.Truth.assertThat
24 import org.junit.Before
25 import org.junit.Test
26 import java.util.concurrent.CountDownLatch
27 import java.util.concurrent.CyclicBarrier
28 import java.util.concurrent.TimeUnit
29 import java.util.concurrent.atomic.AtomicBoolean
30 import java.util.concurrent.atomic.AtomicInteger
31 import java.util.concurrent.atomic.AtomicLong
32 
33 class RequestThrottleTest {
34 
35     private val counter = AtomicInteger(0)
36 
37     private val handler = TestHandler(null)
38 
39     @Before
resetValuesnull40     fun resetValues() {
41         handler.flush()
42         counter.set(0)
43         assertThat(counter.get()).isEqualTo(0)
44     }
45 
46     @Test
simpleThrottlenull47     fun simpleThrottle() {
48         val request = RequestThrottle(handler) {
49             counter.incrementAndGet()
50             true
51         }
52 
53         fun sendRequests() {
54             request.schedule()
55             val thread = startThread { request.schedule() }
56             request.schedule()
57             thread.joinForTest()
58         }
59 
60         sendRequests()
61         handler.flush()
62         assertThat(counter.get()).isEqualTo(1)
63 
64         sendRequests()
65         handler.flush()
66         assertThat(counter.get()).isEqualTo(2)
67     }
68 
69     @Test
exceptionInRequestnull70     fun exceptionInRequest() {
71         val shouldThrow = AtomicBoolean(true)
72         val request = RequestThrottle(handler) {
73             if (shouldThrow.get()) {
74                 throw RuntimeException()
75             }
76             counter.incrementAndGet()
77             true
78         }
79 
80         fun sendRequests() {
81             request.schedule()
82             val thread = startThread { request.schedule() }
83             request.schedule()
84             thread.joinForTest()
85         }
86 
87         sendRequests()
88         try {
89             handler.flush()
90         } catch (ignored: Exception) {
91         }
92         assertThat(counter.get()).isEqualTo(0)
93 
94         shouldThrow.set(false)
95 
96         sendRequests()
97         handler.flush()
98         assertThat(counter.get()).isEqualTo(1)
99     }
100 
101     @Test
scheduleWhileRunningnull102     fun scheduleWhileRunning() {
103         val latchForStartRequest = CountDownLatch(1)
104         val latchForEndRequest = CountDownLatch(1)
105         val request = RequestThrottle(handler) {
106             latchForStartRequest.countDown()
107             counter.incrementAndGet()
108             latchForEndRequest.awaitForTest()
109             true
110         }
111 
112         // Schedule and block a request
113         request.schedule()
114         val handlerThread = startThread { handler.timeAdvance() }
115         latchForStartRequest.awaitForTest()
116 
117         // Hit it with other requests
118         request.schedule()
119         (0..5).map { startThread { request.schedule() } }
120                 .forEach { it.joinForTest() }
121 
122         // Release everything
123         latchForEndRequest.countDown()
124         handlerThread.join()
125         handler.flush()
126 
127         // Ensure another request was run after initial blocking request ends
128         assertThat(counter.get()).isEqualTo(2)
129     }
130 
131     @Test
backoffRetrynull132     fun backoffRetry() {
133         val time = AtomicLong(0)
134         val handler = TestHandler(null) { time.get() }
135         val returnValue = AtomicBoolean(false)
136         val request = RequestThrottle(handler, 3, 1000, 2) {
137             counter.incrementAndGet()
138             returnValue.get()
139         }
140 
141         request.schedule()
142 
143         handler.timeAdvance()
144         handler.pendingMessages.apply {
145             assertThat(size).isEqualTo(1)
146             assertThat(single().sendTime).isAround(1000)
147         }
148 
149         time.set(1000)
150         handler.timeAdvance()
151         handler.pendingMessages.apply {
152             assertThat(size).isEqualTo(1)
153             assertThat(single().sendTime).isAround(3000)
154         }
155 
156         time.set(3000)
157         handler.timeAdvance()
158         handler.pendingMessages.apply {
159             assertThat(size).isEqualTo(1)
160             assertThat(single().sendTime).isAround(7000)
161         }
162 
163         returnValue.set(true)
164         time.set(7000)
165         handler.timeAdvance()
166         assertThat(handler.pendingMessages).isEmpty()
167 
168         // Ensure another request was run after initial blocking request ends
169         assertThat(counter.get()).isEqualTo(4)
170     }
171 
172     @Test
forceWriteMultiplenull173     fun forceWriteMultiple() {
174         val request = RequestThrottle(handler) {
175             counter.incrementAndGet()
176             true
177         }
178 
179         request.runNow()
180         request.runNow()
181         request.runNow()
182 
183         assertThat(counter.get()).isEqualTo(3)
184     }
185 
186     @Test
forceWriteNowWithoutSyncnull187     fun forceWriteNowWithoutSync() {
188         // When forcing a write without synchronizing the request block, 2 instances will be run.
189         // There is no test for "with sync" because any logic to avoid multiple runs is left
190         // entirely up to the caller.
191 
192         val barrierForEndRequest = CyclicBarrier(2)
193         val request = RequestThrottle(handler) {
194             counter.incrementAndGet()
195             barrierForEndRequest.awaitForTest()
196             true
197         }
198 
199         // Schedule and block a request
200         request.schedule()
201         val thread = startThread { handler.timeAdvance() }
202 
203         request.runNow()
204 
205         thread.joinForTest()
206 
207         assertThat(counter.get()).isEqualTo(2)
208     }
209 
CountDownLatchnull210     private fun CountDownLatch.awaitForTest() = assertThat(await(5, TimeUnit.SECONDS)).isTrue()
211     private fun CyclicBarrier.awaitForTest() = await(5, TimeUnit.SECONDS)
212     private fun Thread.joinForTest() = join(5000)
213 
214     private fun startThread(block: () -> Unit) = Thread { block() }.apply { start() }
215 
216     // Float math means time calculations are not exact, so use a loose range
LongSubjectnull217     private fun LongSubject.isAround(value: Long, threshold: Long = 10) =
218             isIn(Range.closed(value - threshold, value + threshold))
219 }
220