blob: e3ba93d64b0f727a35bc83f0f58d7edcd5c731c3 [file] [log] [blame]
Ryan Mitchell61158582020-01-14 12:35:08 -08001/*
2 * Copyright (C) 2019 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
17package android.content.res.loader.test
18
19import android.content.res.AssetManager
20import android.content.res.loader.AssetsProvider
21import android.content.res.loader.DirectoryAssetsProvider
22import android.content.res.loader.ResourcesLoader
23import android.content.res.loader.ResourcesProvider
24import com.google.common.truth.Truth.assertThat
25import org.junit.Before
26import org.junit.Rule
27import org.junit.Test
28import org.junit.rules.TestName
29import org.junit.runner.RunWith
30import org.junit.runners.Parameterized
31import org.mockito.Mockito.anyInt
32import org.mockito.Mockito.anyString
33import org.mockito.Mockito.doAnswer
34import org.mockito.Mockito.doReturn
35import org.mockito.Mockito.eq
36import org.mockito.Mockito.inOrder
37import org.mockito.Mockito.mock
38import java.io.File
39import java.io.FileNotFoundException
40import java.io.IOException
41import java.nio.file.Paths
42
43@RunWith(Parameterized::class)
44class ResourceLoaderAssetsTest : ResourceLoaderTestBase() {
45
46 companion object {
47 private const val BASE_TEST_PATH = "android/content/res/loader/test/file.txt"
48 private const val TEST_TEXT = "some text"
49
50 @JvmStatic
51 @Parameterized.Parameters(name = "{0}")
52 fun parameters(): Array<Array<out Any?>> {
53 val fromInputStream: AssetsProvider.(String) -> Any? = {
54 loadAsset(eq(it), anyInt())
55 }
56
57 val fromFileDescriptor: AssetsProvider.(String) -> Any? = {
58 loadAssetParcelFd(eq(it))
59 }
60
61 val openAsset: AssetManager.() -> String? = {
62 open(BASE_TEST_PATH).reader().readText()
63 }
64
65 val openNonAsset: AssetManager.() -> String? = {
66 openNonAssetFd(BASE_TEST_PATH).readText()
67 }
68
69 return arrayOf(
70 arrayOf("assets", fromInputStream, openAsset),
71 arrayOf("", fromFileDescriptor, openNonAsset)
72 )
73 }
74 }
75
76 @get:Rule
77 val testName = TestName()
78
79 @JvmField
80 @field:Parameterized.Parameter(0)
81 var prefix: String? = null
82
83 @field:Parameterized.Parameter(1)
84 lateinit var loadAssetFunction: AssetsProvider.(String) -> Any?
85
86 @field:Parameterized.Parameter(2)
87 lateinit var openAssetFunction: AssetManager.() -> String?
88
89 private val testPath: String
90 get() = Paths.get(prefix.orEmpty(), BASE_TEST_PATH).toString()
91
92 private fun AssetsProvider.loadAsset() = loadAssetFunction(testPath)
93
94 private fun AssetManager.openAsset() = openAssetFunction()
95
96 private lateinit var testDir: File
97
98 @Before
99 fun setUpTestDir() {
100 testDir = context.filesDir.resolve("DirectoryAssetsProvider_${testName.methodName}")
101 testDir.resolve(testPath).apply { parentFile!!.mkdirs() }.writeText(TEST_TEXT)
102 }
103
104 @Test
105 fun multipleProvidersSearchesBackwards() {
106 // DirectoryResourceLoader relies on a private field and can't be spied directly, so wrap it
107 val assetsProvider = DirectoryAssetsProvider(testDir)
108 val assetProviderWrapper = mock(AssetsProvider::class.java).apply {
109 doAnswer { assetsProvider.loadAsset(it.arguments[0] as String, it.arguments[1] as Int) }
110 .`when`(this).loadAsset(anyString(), anyInt())
111 doAnswer { assetsProvider.loadAssetParcelFd(it.arguments[0] as String) }
112 .`when`(this).loadAssetParcelFd(anyString())
113 }
114
115 val one = ResourcesProvider.empty(assetProviderWrapper)
116 val two = mockProvider {
117 doReturn(null).`when`(it).loadAsset()
118 }
119
120 val loader = ResourcesLoader()
121 loader.providers = listOf(one, two)
122 resources.addLoader(loader)
123
124 assertOpenedAsset()
125 inOrder(two.assetsProvider, one.assetsProvider).apply {
126 verify(two.assetsProvider)?.loadAsset()
127 verify(one.assetsProvider)?.loadAsset()
128 }
129 }
130
131 @Test
132 fun multipleLoadersSearchesBackwards() {
133 // DirectoryResourceLoader relies on a private field and can't be spied directly, so wrap it
134 val assetsProvider = DirectoryAssetsProvider(testDir)
135 val assetProviderWrapper = mock(AssetsProvider::class.java).apply {
136 doAnswer { assetsProvider.loadAsset(it.arguments[0] as String, it.arguments[1] as Int) }
137 .`when`(this).loadAsset(anyString(), anyInt())
138 doAnswer { assetsProvider.loadAssetParcelFd(it.arguments[0] as String) }
139 .`when`(this).loadAssetParcelFd(anyString())
140 }
141
142 val one = ResourcesProvider.empty(assetProviderWrapper)
143 val two = mockProvider {
144 doReturn(null).`when`(it).loadAsset()
145 }
146
147 val loader1 = ResourcesLoader()
148 loader1.addProvider(one)
149 val loader2 = ResourcesLoader()
150 loader2.addProvider(two)
151
152 resources.loaders = listOf(loader1, loader2)
153
154 assertOpenedAsset()
155 inOrder(two.assetsProvider, one.assetsProvider).apply {
156 verify(two.assetsProvider)?.loadAsset()
157 verify(one.assetsProvider)?.loadAsset()
158 }
159 }
160
161 @Test(expected = FileNotFoundException::class)
162 fun failToFindThrowsFileNotFound() {
163 val assetsProvider1 = mock(AssetsProvider::class.java).apply {
164 doReturn(null).`when`(this).loadAsset()
165 }
166 val assetsProvider2 = mock(AssetsProvider::class.java).apply {
167 doReturn(null).`when`(this).loadAsset()
168 }
169
170 val loader = ResourcesLoader()
171 val one = ResourcesProvider.empty(assetsProvider1)
172 val two = ResourcesProvider.empty(assetsProvider2)
173 resources.addLoader(loader)
174 loader.providers = listOf(one, two)
175
176 assertOpenedAsset()
177 }
178
179 @Test
180 fun throwingIOExceptionIsSkipped() {
181 val assetsProvider1 = DirectoryAssetsProvider(testDir)
182 val assetsProvider2 = mock(AssetsProvider::class.java).apply {
183 doAnswer { throw IOException() }.`when`(this).loadAsset()
184 }
185
186 val loader = ResourcesLoader()
187 val one = ResourcesProvider.empty(assetsProvider1)
188 val two = ResourcesProvider.empty(assetsProvider2)
189 resources.addLoader(loader)
190 loader.providers = listOf(one, two)
191
192 assertOpenedAsset()
193 }
194
195 @Test(expected = IllegalStateException::class)
196 fun throwingNonIOExceptionCausesFailure() {
197 val assetsProvider1 = DirectoryAssetsProvider(testDir)
198 val assetsProvider2 = mock(AssetsProvider::class.java).apply {
199 doAnswer { throw IllegalStateException() }.`when`(this).loadAsset()
200 }
201
202 val loader = ResourcesLoader()
203 val one = ResourcesProvider.empty(assetsProvider1)
204 val two = ResourcesProvider.empty(assetsProvider2)
205 resources.addLoader(loader)
206 loader.providers = listOf(one, two)
207
208 assertOpenedAsset()
209 }
210
211 private fun mockProvider(block: (AssetsProvider) -> Unit = {}): ResourcesProvider {
212 return ResourcesProvider.empty(mock(AssetsProvider::class.java).apply {
213 block.invoke(this)
214 })
215 }
216
217 private fun assertOpenedAsset() {
218 assertThat(resources.assets.openAsset()).isEqualTo(TEST_TEXT)
219 }
220}