1 /*
2  * Copyright (C) 2020 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 android.content.pm.parsing.result
18 
19 import android.content.pm.PackageManager
20 import android.os.Build
21 import android.platform.test.annotations.Presubmit
22 import com.google.common.truth.Truth.assertThat
23 import org.junit.After
24 import org.junit.Assume.assumeFalse
25 import org.junit.Before
26 import org.junit.BeforeClass
27 import org.junit.Test
28 import org.mockito.ArgumentMatchers.anyLong
29 import org.mockito.Mockito.anyInt
30 import org.mockito.Mockito.anyString
31 import org.mockito.Mockito.never
32 import org.mockito.Mockito.spy
33 import org.mockito.Mockito.times
34 import org.mockito.Mockito.verify
35 import org.mockito.Mockito.verifyNoMoreInteractions
36 import java.io.IOException
37 
38 @Presubmit
39 class ParseInputAndResultTest {
40 
41     companion object {
42 
43         private const val TEST_PACKAGE = "com.android.test"
44 
45         private const val ENABLED_ERROR = 11L
46         private const val DISABLED_ERROR = 22L
47 
48         @JvmStatic
49         @BeforeClass
assumeNotDebugnull50         fun assumeNotDebug() {
51             // None of these tests consider cases where debugging logic is enabled
52             assumeFalse(ParseTypeImpl.DEBUG_FILL_STACK_TRACE)
53             assumeFalse(ParseTypeImpl.DEBUG_LOG_ON_ERROR)
54             assumeFalse(ParseTypeImpl.DEBUG_THROW_ALL_ERRORS)
55         }
56     }
57 
58     private lateinit var mockCallback: ParseInput.Callback
59     private lateinit var input: ParseInput
60 
61     @Before
createInputnull62     fun createInput() {
63         // Use an open class instead off a lambda so it can be spied
64         open class TestCallback : ParseInput.Callback {
65             override fun isChangeEnabled(changeId: Long, pkgName: String, targetSdk: Int): Boolean {
66                 return when (changeId) {
67                     ENABLED_ERROR -> targetSdk > Build.VERSION_CODES.Q
68                     DISABLED_ERROR -> false
69                     else -> throw IllegalStateException("changeId $changeId is not mocked for test")
70                 }
71             }
72         }
73 
74         mockCallback = spy(TestCallback())
75         input = ParseTypeImpl(mockCallback)
76     }
77 
78     @Test
errorCodenull79     fun errorCode() {
80         val errorCode = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE
81         val result = input.error<Any?>(errorCode)
82         assertError(result)
83         assertThat(result.errorCode).isEqualTo(errorCode)
84         assertThat(result.errorMessage).isNull()
85         assertThat(result.exception).isNull()
86     }
87 
88     @Test
errorMessagenull89     fun errorMessage() {
90         val errorMessage = "Test error"
91         val result = input.error<Any?>(errorMessage)
92         assertError(result)
93         assertThat(result.errorCode).isNotEqualTo(PackageManager.INSTALL_SUCCEEDED)
94         assertThat(result.errorMessage).isEqualTo(errorMessage)
95         assertThat(result.exception).isNull()
96     }
97 
98     @Test
errorCodeAndMessagenull99     fun errorCodeAndMessage() {
100         val errorCode = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE
101         val errorMessage = "Test error"
102         val result = input.error<Any?>(errorCode, errorMessage)
103         assertError(result)
104         assertThat(result.errorCode).isEqualTo(errorCode)
105         assertThat(result.errorMessage).isEqualTo(errorMessage)
106         assertThat(result.exception).isNull()
107     }
108 
109     @Test
errorCodeAndMessageAndExceptionnull110     fun errorCodeAndMessageAndException() {
111         val errorCode = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE
112         val errorMessage = "Test error"
113         val exception = IOException()
114         val result = input.error<Any?>(errorCode, errorMessage, exception)
115         assertError(result)
116         assertThat(result.errorCode).isEqualTo(errorCode)
117         assertThat(result.errorMessage).isEqualTo(errorMessage)
118         assertThat(result.exception).isSameInstanceAs(exception)
119     }
120 
121     @Test
errorCarryResultnull122     fun errorCarryResult() {
123         val errorCode = PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE
124         val errorMessage = "Test error"
125         val exception = IOException()
126         val result = input.error<Any?>(errorCode, errorMessage, exception)
127         assertError(result)
128         assertThat(result.errorCode).isEqualTo(errorCode)
129         assertThat(result.errorMessage).isEqualTo(errorMessage)
130         assertThat(result.exception).isSameInstanceAs(exception)
131 
132         val carriedResult = input.error<Int>(result)
133         assertError(carriedResult)
134         assertThat(carriedResult.errorCode).isEqualTo(errorCode)
135         assertThat(carriedResult.errorMessage).isEqualTo(errorMessage)
136         assertThat(carriedResult.exception).isSameInstanceAs(exception)
137     }
138 
139     @Test
successnull140     fun success() {
141         val value = "Test success"
142         assertSuccess(value, input.success(value))
143     }
144 
145     @Test
deferErrorEnableFirstSdkQnull146     fun deferErrorEnableFirstSdkQ() {
147         assertSuccess(input.enableDeferredError(TEST_PACKAGE, Build.VERSION_CODES.Q))
148 
149         assertSuccess(input.deferError("Test error", ENABLED_ERROR))
150     }
151 
152     @Test
deferErrorEnableLastSdkQnull153     fun deferErrorEnableLastSdkQ() {
154         assertSuccess(input.deferError("Test error", ENABLED_ERROR))
155 
156         assertSuccess(input.enableDeferredError(TEST_PACKAGE, Build.VERSION_CODES.Q))
157     }
158 
159     @Test
deferErrorEnableFirstSdkRnull160     fun deferErrorEnableFirstSdkR() {
161         val error = "Test error"
162         assertSuccess(input.enableDeferredError(TEST_PACKAGE, Build.VERSION_CODES.R))
163 
164         val deferResult = input.deferError(error, ENABLED_ERROR)
165         assertError(deferResult)
166         assertThat(deferResult.errorCode).isNotEqualTo(PackageManager.INSTALL_SUCCEEDED)
167         assertThat(deferResult.errorMessage).isEqualTo(error)
168         assertThat(deferResult.exception).isNull()
169     }
170 
171     @Test
deferErrorEnableLastSdkRnull172     fun deferErrorEnableLastSdkR() {
173         val error = "Test error"
174         assertSuccess(input.deferError(error, ENABLED_ERROR))
175 
176         val result = input.enableDeferredError(TEST_PACKAGE, Build.VERSION_CODES.R)
177         assertError(result)
178         assertThat(result.errorCode).isNotEqualTo(PackageManager.INSTALL_SUCCEEDED)
179         assertThat(result.errorMessage).isEqualTo(error)
180         assertThat(result.exception).isNull()
181     }
182 
183     @Test
enableDeferredErrorAndSuccessSdkQnull184     fun enableDeferredErrorAndSuccessSdkQ() {
185         val value = "Test success"
186         assertSuccess(input.enableDeferredError(TEST_PACKAGE, Build.VERSION_CODES.Q))
187 
188         assertSuccess(value, input.success(value))
189     }
190 
191     @Test
enableDeferredErrorAndSuccessSdkRnull192     fun enableDeferredErrorAndSuccessSdkR() {
193         val value = "Test success"
194         assertSuccess(input.enableDeferredError(TEST_PACKAGE, Build.VERSION_CODES.R))
195 
196         assertSuccess(value, input.success(value))
197     }
198 
199     @Test
multipleDeferErrorKeepsFirstnull200     fun multipleDeferErrorKeepsFirst() {
201         val errorOne = "Test error one"
202         val errorTwo = "Test error two"
203 
204         assertSuccess(input.deferError(errorOne, ENABLED_ERROR))
205         assertSuccess(input.deferError(errorTwo, ENABLED_ERROR))
206 
207         val result = input.enableDeferredError(TEST_PACKAGE, Build.VERSION_CODES.R)
208         assertError(result)
209         assertThat(result.errorCode).isNotEqualTo(PackageManager.INSTALL_SUCCEEDED)
210         assertThat(result.errorMessage).isEqualTo(errorOne)
211         assertThat(result.exception).isNull()
212     }
213 
214     @Test
multipleDisabledErrorsQueriesOnceEnableFirstnull215     fun multipleDisabledErrorsQueriesOnceEnableFirst() {
216         val errorOne = "Test error one"
217         val errorTwo = "Test error two"
218 
219         assertSuccess(input.enableDeferredError(TEST_PACKAGE, Build.VERSION_CODES.R))
220 
221         assertSuccess(input.deferError(errorOne, DISABLED_ERROR))
222 
223         verify(mockCallback, times(1)).isChangeEnabled(anyLong(), anyString(), anyInt())
224 
225         assertSuccess(input.deferError(errorTwo, DISABLED_ERROR))
226 
227         verifyNoMoreInteractions(mockCallback)
228     }
229 
230     @Test
multipleDisabledErrorsQueriesOnceEnableSecondnull231     fun multipleDisabledErrorsQueriesOnceEnableSecond() {
232         val errorOne = "Test error one"
233         val errorTwo = "Test error two"
234 
235         assertSuccess(input.deferError(errorOne, DISABLED_ERROR))
236 
237         verify(mockCallback, never()).isChangeEnabled(anyLong(), anyString(), anyInt())
238 
239         assertSuccess(input.enableDeferredError(TEST_PACKAGE, Build.VERSION_CODES.R))
240 
241         verify(mockCallback, times(1)).isChangeEnabled(anyLong(), anyString(), anyInt())
242 
243         assertSuccess(input.deferError(errorTwo, DISABLED_ERROR))
244 
245         verifyNoMoreInteractions(mockCallback)
246     }
247 
248     @After
verifyResetnull249     fun verifyReset() {
250         var result = (input as ParseTypeImpl).reset() as ParseResult<*>
251         result.assertReset()
252 
253         // The deferred error is not directly accessible, so attempt to re-enable the deferred
254         // error and assert it was also reset.
255         result = input.enableDeferredError(TEST_PACKAGE, Build.VERSION_CODES.R)
256         result.assertReset()
257     }
258 
assertSuccessnull259     private fun assertSuccess(result: ParseResult<*>) = assertSuccess(null, result)
260 
261     private fun assertSuccess(expected: Any? = null, result: ParseResult<*>) {
262         assertThat(result.isError).isFalse()
263         assertThat(result.isSuccess).isTrue()
264         assertThat(result.result).isSameInstanceAs(expected)
265         assertThat(result.errorCode).isEqualTo(PackageManager.INSTALL_SUCCEEDED)
266         assertThat(result.errorMessage).isNull()
267         assertThat(result.exception).isNull()
268     }
269 
assertErrornull270     private fun assertError(result: ParseResult<*>) {
271         assertThat(result.isError).isTrue()
272         assertThat(result.isSuccess).isFalse()
273         assertThat(result.result).isNull()
274     }
275 
ParseResultnull276     private fun ParseResult<*>.assertReset() {
277         assertThat(this.isSuccess).isTrue()
278         assertThat(this.isError).isFalse()
279         assertThat(this.errorCode).isEqualTo(PackageManager.INSTALL_SUCCEEDED)
280         assertThat(this.errorMessage).isNull()
281         assertThat(this.exception).isNull()
282     }
283 }
284