1 /*
<lambda>null2  * Copyright (C) 2023 Square, Inc.
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 okio.assetfilesystem
17 
18 import androidx.test.platform.app.InstrumentationRegistry
19 import assertk.assertThat
20 import assertk.assertions.containsAll
21 import assertk.assertions.containsExactly
22 import assertk.assertions.hasMessage
23 import assertk.assertions.isEmpty
24 import assertk.assertions.isEqualTo
25 import assertk.assertions.isFalse
26 import assertk.assertions.isNotNull
27 import assertk.assertions.isNull
28 import assertk.assertions.isTrue
29 import kotlin.test.assertFailsWith
30 import okio.Buffer
31 import okio.BufferedSource
32 import okio.FileHandle
33 import okio.FileNotFoundException
34 import okio.IOException
35 import okio.Path.Companion.toPath
36 import okio.buffer
37 import org.junit.Test
38 
39 class AssetFileSystemTest {
40   private val context = InstrumentationRegistry.getInstrumentation().context
41   private val fs = context.assets.asFileSystem()
42 
43   @Test fun canonicalizeValid() {
44     assertThat(fs.canonicalize("/".toPath())).isEqualTo("/".toPath())
45     assertThat(fs.canonicalize(".".toPath())).isEqualTo("/".toPath())
46     assertThat(fs.canonicalize("not/a/path/../../..".toPath())).isEqualTo("/".toPath())
47     assertThat(fs.canonicalize("file.txt".toPath())).isEqualTo("/file.txt".toPath())
48     assertThat(fs.canonicalize("stuff/../file.txt".toPath())).isEqualTo("/file.txt".toPath())
49     assertThat(fs.canonicalize("dir".toPath())).isEqualTo("/dir".toPath())
50     assertThat(fs.canonicalize("dir/whevs/..".toPath())).isEqualTo("/dir".toPath())
51     assertThat(fs.canonicalize("dir/nested.txt".toPath())).isEqualTo("/dir/nested.txt".toPath())
52     assertThat(fs.canonicalize("dir/whevs/../nested.txt".toPath())).isEqualTo("/dir/nested.txt".toPath())
53   }
54 
55   @Test fun canonicalizeInvalidThrows() {
56     assertFailsWith<FileNotFoundException> {
57       fs.canonicalize("not/a/path".toPath())
58     }
59   }
60 
61   @Test fun listRoot() {
62     val list = fs.list("/".toPath())
63     assertThat(list).containsAll(
64       "dir".toPath(),
65       "file.txt".toPath(),
66       "moby10b.txt".toPath(),
67     )
68   }
69 
70   @Test fun listRootCanonicalizes() {
71     val list = fs.list("foo/bar/../..".toPath())
72     assertThat(list).containsAll(
73       "dir".toPath(),
74       "file.txt".toPath(),
75       "moby10b.txt".toPath(),
76     )
77   }
78 
79   @Test fun listDirectory() {
80     val list = fs.list("dir".toPath())
81     assertThat(list).containsExactly("nested.txt".toPath())
82   }
83 
84   @Test fun listDirectoryCanonicalizes() {
85     val list = fs.list("dir/not/real/../..".toPath())
86     assertThat(list).containsExactly("nested.txt".toPath())
87   }
88 
89   @Test fun listNonExistentDirectoryThrows() {
90     assertFailsWith<FileNotFoundException> {
91       fs.list("nope/".toPath())
92     }
93   }
94 
95   @Test fun listFileThrows() {
96     assertFailsWith<FileNotFoundException> {
97       fs.list("dir/nested.txt".toPath())
98     }
99   }
100 
101   @Test fun listOrNullDirectory() {
102     val list = fs.listOrNull("dir".toPath())
103     assertThat(list).isNotNull().containsExactly("nested.txt".toPath())
104   }
105 
106   @Test fun listOrNullDirectoryCanonicalizes() {
107     val list = fs.listOrNull("dir/not/real/../..".toPath())
108     assertThat(list).isNotNull().containsExactly("nested.txt".toPath())
109   }
110 
111   @Test fun listOrNullNonExistentDirectory() {
112     val list = fs.listOrNull("nope".toPath())
113     assertThat(list).isNull()
114   }
115 
116   @Test fun listOrNullFile() {
117     val list = fs.listOrNull("dir/nested.txt".toPath())
118     assertThat(list).isNull()
119   }
120 
121   @Test fun metadataFile() {
122     val metadata = fs.metadataOrNull("file.txt".toPath())!!
123 
124     // Data we can get:
125     assertThat(metadata.isRegularFile).isTrue()
126     assertThat(metadata.isDirectory).isFalse()
127 
128     // Data we cannot get or is impossible:
129     assertThat(metadata.size).isNull()
130     assertThat(metadata.symlinkTarget).isNull()
131     assertThat(metadata.createdAtMillis).isNull()
132     assertThat(metadata.lastModifiedAtMillis).isNull()
133     assertThat(metadata.lastAccessedAtMillis).isNull()
134     assertThat(metadata.extras).isEmpty()
135   }
136 
137   @Test fun metadataDirectory() {
138     val metadata = fs.metadataOrNull("dir".toPath())!!
139 
140     // Data we can get:
141     assertThat(metadata.isRegularFile).isFalse()
142     assertThat(metadata.isDirectory).isTrue()
143 
144     // Data we cannot get or is impossible:
145     assertThat(metadata.symlinkTarget).isNull()
146     assertThat(metadata.size).isNull()
147     assertThat(metadata.createdAtMillis).isNull()
148     assertThat(metadata.lastModifiedAtMillis).isNull()
149     assertThat(metadata.lastAccessedAtMillis).isNull()
150     assertThat(metadata.extras).isEmpty()
151   }
152 
153   @Test fun metadataDirectoryCanonicalizes() {
154     val metadata = fs.metadataOrNull("dir/not/real/../..".toPath())!!
155     assertThat(metadata.isDirectory).isTrue()
156   }
157 
158   @Test fun metadataNonExistentPath() {
159     val metadata = fs.metadataOrNull("not/a/path".toPath())
160     assertThat(metadata).isNull()
161   }
162 
163   @Test fun sourceFile() {
164     val file = fs.source("file.txt".toPath()).buffer().use(BufferedSource::readUtf8)
165     assertThat(file).isEqualTo("File!\n")
166     val nested = fs.source("dir/nested.txt".toPath()).buffer().use(BufferedSource::readUtf8)
167     assertThat(nested).isEqualTo("Nested!\n")
168   }
169 
170   @Test fun sourceDirectory() {
171     assertFailsWith<FileNotFoundException> {
172       fs.source("dir".toPath())
173     }
174   }
175 
176   @Test fun sourceNonExistent() {
177     assertFailsWith<FileNotFoundException> {
178       fs.source("not/a/path".toPath())
179     }
180   }
181 
182   @Test fun openReadOnlyInvalidThrows() {
183     assertFailsWith<FileNotFoundException> {
184       fs.openReadOnly("not/a/path".toPath())
185     }
186   }
187 
188   @Test fun openReadOnlyDirectoryThrows() {
189     assertFailsWith<FileNotFoundException> {
190       fs.openReadOnly("dir".toPath())
191     }
192   }
193 
194   @Test fun openReadOnlySize() {
195     val smallSize = fs.openReadOnly("file.txt".toPath()).use(FileHandle::size)
196     assertThat(smallSize).isEqualTo(6)
197 
198     val mobySize = fs.openReadOnly("moby10b.txt".toPath()).use(FileHandle::size)
199     assertThat(mobySize).isEqualTo(1232923)
200   }
201 
202   @Test fun openReadOnlyRandomAccessForward() {
203     fs.openReadOnly("moby10b.txt".toPath()).use { handle ->
204       val buffer = Buffer()
205       handle.read(34251, buffer, 16)
206       assertThat(buffer.readUtf8()).isEqualTo("Call me Ishmael.")
207       handle.read(148605, buffer, 49)
208       assertThat(buffer.readUtf8()).isEqualTo("It is not down in any map; true places never are.")
209       handle.read(1051694, buffer, 50)
210       assertThat(buffer.readUtf8()).isEqualTo("his forehead's veins swelled like overladen brooks")
211       handle.read(148605, buffer, 49)
212       assertThat(buffer.readUtf8()).isEqualTo("It is not down in any map; true places never are.")
213       handle.read(34251, buffer, 16)
214       assertThat(buffer.readUtf8()).isEqualTo("Call me Ishmael.")
215     }
216   }
217 
218   @Test fun openReadOnlyRandomAccessThenSize() {
219     fs.openReadOnly("moby10b.txt".toPath()).use { handle ->
220       val buffer = Buffer()
221       handle.read(34251, buffer, 16)
222       assertThat(buffer.readUtf8()).isEqualTo("Call me Ishmael.")
223 
224       assertThat(handle.size()).isEqualTo(1232923)
225     }
226   }
227 
228   @Test fun openReadOnlyFlushThrows() {
229     fs.openReadOnly("file.txt".toPath()).use { handle ->
230       val t = assertFailsWith<IllegalStateException> {
231         handle.flush()
232       }
233       assertThat(t).hasMessage("file handle is read-only")
234     }
235   }
236 
237   @Test fun openReadOnlyResizeThrows() {
238     fs.openReadOnly("file.txt".toPath()).use { handle ->
239       val t = assertFailsWith<IllegalStateException> {
240         handle.resize(10L)
241       }
242       assertThat(t).hasMessage("file handle is read-only")
243     }
244   }
245 
246   @Test fun openReadOnlyWriteThrows() {
247     fs.openReadOnly("file.txt".toPath()).use { handle ->
248       val t = assertFailsWith<IllegalStateException> {
249         handle.write(0, Buffer().writeUtf8("Sup"), 3)
250       }
251       assertThat(t).hasMessage("file handle is read-only")
252     }
253   }
254 
255   @Test fun sinkThrows() {
256     val t = assertFailsWith<IOException> {
257       fs.sink("file.txt".toPath())
258     }
259     assertThat(t).hasMessage("asset file systems are read-only")
260   }
261 
262   @Test fun appendingSinkThrows() {
263     val t = assertFailsWith<IOException> {
264       fs.appendingSink("file.txt".toPath())
265     }
266     assertThat(t).hasMessage("asset file systems are read-only")
267   }
268 
269   @Test fun createDirectoryThrows() {
270     val t = assertFailsWith<IOException> {
271       fs.createDirectory("new-dir".toPath(), mustCreate = true)
272     }
273     assertThat(t).hasMessage("asset file systems are read-only")
274   }
275 
276   @Test fun atomicMoveThrows() {
277     val t = assertFailsWith<IOException> {
278       fs.atomicMove("file.txt".toPath(), "new-file.txt".toPath())
279     }
280     assertThat(t).hasMessage("asset file systems are read-only")
281   }
282 
283   @Test fun deleteThrows() {
284     val t = assertFailsWith<IOException> {
285       fs.delete("file.txt".toPath())
286     }
287     assertThat(t).hasMessage("asset file systems are read-only")
288   }
289 
290   @Test fun createSymlinkThrows() {
291     val t = assertFailsWith<IOException> {
292       fs.createSymlink("file.txt".toPath(), "new-file.txt".toPath())
293     }
294     assertThat(t).hasMessage("asset file systems are read-only")
295   }
296 
297   @Test fun openReadWriteThrows() {
298     val t = assertFailsWith<IOException> {
299       fs.openReadWrite("file.txt".toPath())
300     }
301     assertThat(t).hasMessage("asset file systems are read-only")
302   }
303 }
304