xref: /aosp_15_r20/external/okio/okio/src/commonTest/kotlin/okio/CommonOptionsTest.kt (revision f9742813c14b702d71392179818a9e591da8620c)
1 /*
2  * Copyright (C) 2018 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
17 
18 import kotlin.test.Test
19 import kotlin.test.assertEquals
20 import kotlin.test.assertFailsWith
21 import kotlin.test.fail
22 import okio.ByteString.Companion.encodeUtf8
23 
24 class CommonOptionsTest {
25   /** Confirm that options prefers the first-listed option, not the longest or shortest one. */
optionOrderTakesPrecedencenull26   @Test fun optionOrderTakesPrecedence() {
27     assertSelect("abcdefg", 0, "abc", "abcdef")
28     assertSelect("abcdefg", 0, "abcdef", "abc")
29   }
30 
simpleOptionsTrienull31   @Test fun simpleOptionsTrie() {
32     assertEquals(
33       utf8Options("hotdog", "hoth", "hot").trieString(),
34       """
35       |hot
36       |   -> 2
37       |   d
38       |    og -> 0
39       |   h -> 1
40       |
41       """.trimMargin(),
42     )
43   }
44 
realisticOptionsTrienull45   @Test fun realisticOptionsTrie() {
46     // These are the fields of OkHttpClient in 3.10.
47     val options = utf8Options(
48       "dispatcher",
49       "proxy",
50       "protocols",
51       "connectionSpecs",
52       "interceptors",
53       "networkInterceptors",
54       "eventListenerFactory",
55       "proxySelector", // No index 7 in the trie because 'proxy' is a prefix!
56       "cookieJar",
57       "cache",
58       "internalCache",
59       "socketFactory",
60       "sslSocketFactory",
61       "certificateChainCleaner",
62       "hostnameVerifier",
63       "certificatePinner",
64       "proxyAuthenticator", // No index 16 in the trie because 'proxy' is a prefix!
65       "authenticator",
66       "connectionPool",
67       "dns",
68       "followSslRedirects",
69       "followRedirects",
70       "retryOnConnectionFailure",
71       "connectTimeout",
72       "readTimeout",
73       "writeTimeout",
74       "pingInterval",
75     )
76     assertEquals(
77       options.trieString(),
78       """
79       |a
80       | uthenticator -> 17
81       |c
82       | a
83       |  che -> 9
84       | e
85       |  rtificate
86       |           C
87       |            hainCleaner -> 13
88       |           P
89       |            inner -> 15
90       | o
91       |  n
92       |   nect
93       |       T
94       |        imeout -> 23
95       |       i
96       |        on
97       |          P
98       |           ool -> 18
99       |          S
100       |           pecs -> 3
101       |  o
102       |   kieJar -> 8
103       |d
104       | i
105       |  spatcher -> 0
106       | n
107       |  s -> 19
108       |e
109       | ventListenerFactory -> 6
110       |f
111       | ollow
112       |      R
113       |       edirects -> 21
114       |      S
115       |       slRedirects -> 20
116       |h
117       | ostnameVerifier -> 14
118       |i
119       | nter
120       |     c
121       |      eptors -> 4
122       |     n
123       |      alCache -> 10
124       |n
125       | etworkInterceptors -> 5
126       |p
127       | i
128       |  ngInterval -> 26
129       | r
130       |  o
131       |   t
132       |    ocols -> 2
133       |   x
134       |    y -> 1
135       |r
136       | e
137       |  a
138       |   dTimeout -> 24
139       |  t
140       |   ryOnConnectionFailure -> 22
141       |s
142       | o
143       |  cketFactory -> 11
144       | s
145       |  lSocketFactory -> 12
146       |w
147       | riteTimeout -> 25
148       |
149       """.trimMargin(),
150     )
151     assertSelect("", -1, options)
152     assertSelect("a", -1, options)
153     assertSelect("eventListenerFactor", -1, options)
154     assertSelect("dnst", 19, options)
155     assertSelect("proxyproxy", 1, options)
156     assertSelect("prox", -1, options)
157 
158     assertSelect("dispatcher", 0, options)
159     assertSelect("proxy", 1, options)
160     assertSelect("protocols", 2, options)
161     assertSelect("connectionSpecs", 3, options)
162     assertSelect("interceptors", 4, options)
163     assertSelect("networkInterceptors", 5, options)
164     assertSelect("eventListenerFactory", 6, options)
165     assertSelect("proxySelector", 1, options) // 'proxy' is a prefix.
166     assertSelect("cookieJar", 8, options)
167     assertSelect("cache", 9, options)
168     assertSelect("internalCache", 10, options)
169     assertSelect("socketFactory", 11, options)
170     assertSelect("sslSocketFactory", 12, options)
171     assertSelect("certificateChainCleaner", 13, options)
172     assertSelect("hostnameVerifier", 14, options)
173     assertSelect("certificatePinner", 15, options)
174     assertSelect("proxyAuthenticator", 1, options) // 'proxy' is a prefix.
175     assertSelect("authenticator", 17, options)
176     assertSelect("connectionPool", 18, options)
177     assertSelect("dns", 19, options)
178     assertSelect("followSslRedirects", 20, options)
179     assertSelect("followRedirects", 21, options)
180     assertSelect("retryOnConnectionFailure", 22, options)
181     assertSelect("connectTimeout", 23, options)
182     assertSelect("readTimeout", 24, options)
183     assertSelect("writeTimeout", 25, options)
184     assertSelect("pingInterval", 26, options)
185   }
186 
emptyOptionsnull187   @Test fun emptyOptions() {
188     val options = utf8Options()
189     assertSelect("", -1, options)
190     assertSelect("a", -1, options)
191     assertSelect("abc", -1, options)
192   }
193 
emptyStringInOptionsTrienull194   @Test fun emptyStringInOptionsTrie() {
195     assertFailsWith<IllegalArgumentException> {
196       utf8Options("")
197     }
198     assertFailsWith<IllegalArgumentException> {
199       utf8Options("abc", "")
200     }
201   }
202 
multipleIdenticalValuesnull203   @Test fun multipleIdenticalValues() {
204     try {
205       utf8Options("abc", "abc")
206       fail()
207     } catch (expected: IllegalArgumentException) {
208       assertEquals(expected.message, "duplicate option: [text=abc]")
209     }
210   }
211 
prefixesAreStrippednull212   @Test fun prefixesAreStripped() {
213     val options = utf8Options("abcA", "abc", "abcB")
214     assertEquals(
215       options.trieString(),
216       """
217       |abc
218       |   -> 1
219       |   A -> 0
220       |
221       """.trimMargin(),
222     )
223     assertSelect("abc", 1, options)
224     assertSelect("abcA", 0, options)
225     assertSelect("abcB", 1, options)
226     assertSelect("abcC", 1, options)
227     assertSelect("ab", -1, options)
228   }
229 
multiplePrefixesAreStrippednull230   @Test fun multiplePrefixesAreStripped() {
231     assertEquals(
232       utf8Options("a", "ab", "abc", "abcd", "abcde").trieString(),
233       """
234       |a -> 0
235       |
236       """.trimMargin(),
237     )
238     assertEquals(
239       utf8Options("abc", "a", "ab", "abe", "abcd", "abcf").trieString(),
240       """
241       |a
242       | -> 1
243       | bc -> 0
244       |
245       """.trimMargin(),
246     )
247     assertEquals(
248       utf8Options("abc", "ab", "a").trieString(),
249       """
250       |a
251       | -> 2
252       | b
253       |  -> 1
254       |  c -> 0
255       |
256       """.trimMargin(),
257     )
258     assertEquals(
259       utf8Options("abcd", "abce", "abc", "abcf", "abcg").trieString(),
260       """
261       |abc
262       |   -> 2
263       |   d -> 0
264       |   e -> 1
265       |
266       """.trimMargin(),
267     )
268   }
269 
scannull270   @Test fun scan() {
271     val options = utf8Options("abc")
272     assertSelect("abcde", 0, options)
273   }
274 
scanReturnsPrefixnull275   @Test fun scanReturnsPrefix() {
276     val options = utf8Options("abcdefg", "ab")
277     assertSelect("ab", 1, options)
278     assertSelect("abcd", 1, options)
279     assertSelect("abcdefg", 0, options)
280     assertSelect("abcdefghi", 0, options)
281     assertSelect("abcdhi", 1, options)
282   }
283 
selectnull284   @Test fun select() {
285     val options = utf8Options("a", "b", "c")
286     assertSelect("a", 0, options)
287     assertSelect("b", 1, options)
288     assertSelect("c", 2, options)
289     assertSelect("d", -1, options)
290     assertSelect("aa", 0, options)
291     assertSelect("bb", 1, options)
292     assertSelect("cc", 2, options)
293     assertSelect("dd", -1, options)
294   }
295 
selectSelectnull296   @Test fun selectSelect() {
297     val options = utf8Options("aa", "ab", "ba", "bb")
298     assertSelect("a", -1, options)
299     assertSelect("b", -1, options)
300     assertSelect("c", -1, options)
301     assertSelect("aa", 0, options)
302     assertSelect("ab", 1, options)
303     assertSelect("ac", -1, options)
304     assertSelect("ba", 2, options)
305     assertSelect("bb", 3, options)
306     assertSelect("bc", -1, options)
307     assertSelect("ca", -1, options)
308     assertSelect("cb", -1, options)
309     assertSelect("cc", -1, options)
310   }
311 
selectScannull312   @Test fun selectScan() {
313     val options = utf8Options("abcd", "defg")
314     assertSelect("a", -1, options)
315     assertSelect("d", -1, options)
316     assertSelect("h", -1, options)
317     assertSelect("ab", -1, options)
318     assertSelect("ae", -1, options)
319     assertSelect("de", -1, options)
320     assertSelect("db", -1, options)
321     assertSelect("hi", -1, options)
322     assertSelect("abcd", 0, options)
323     assertSelect("aefg", -1, options)
324     assertSelect("defg", 1, options)
325     assertSelect("dbcd", -1, options)
326     assertSelect("hijk", -1, options)
327     assertSelect("abcdh", 0, options)
328     assertSelect("defgh", 1, options)
329     assertSelect("hijkl", -1, options)
330   }
331 
scanSelectnull332   @Test fun scanSelect() {
333     val options = utf8Options("abcd", "abce")
334     assertSelect("a", -1, options)
335     assertSelect("f", -1, options)
336     assertSelect("abc", -1, options)
337     assertSelect("abf", -1, options)
338     assertSelect("abcd", 0, options)
339     assertSelect("abce", 1, options)
340     assertSelect("abcf", -1, options)
341     assertSelect("abcdf", 0, options)
342     assertSelect("abcef", 1, options)
343   }
344 
scanSpansSegmentsnull345   @Test fun scanSpansSegments() {
346     val options = utf8Options("abcd")
347     assertSelect(bufferWithSegments("a", "bcd"), 0, options)
348     assertSelect(bufferWithSegments("a", "bcde"), 0, options)
349     assertSelect(bufferWithSegments("ab", "cd"), 0, options)
350     assertSelect(bufferWithSegments("ab", "cde"), 0, options)
351     assertSelect(bufferWithSegments("abc", "d"), 0, options)
352     assertSelect(bufferWithSegments("abc", "de"), 0, options)
353     assertSelect(bufferWithSegments("abcd", "e"), 0, options)
354     assertSelect(bufferWithSegments("a", "bce"), -1, options)
355     assertSelect(bufferWithSegments("a", "bce"), -1, options)
356     assertSelect(bufferWithSegments("ab", "ce"), -1, options)
357     assertSelect(bufferWithSegments("ab", "ce"), -1, options)
358     assertSelect(bufferWithSegments("abc", "e"), -1, options)
359     assertSelect(bufferWithSegments("abc", "ef"), -1, options)
360     assertSelect(bufferWithSegments("abce", "f"), -1, options)
361   }
362 
selectSpansSegmentsnull363   @Test fun selectSpansSegments() {
364     val options = utf8Options("aa", "ab", "ba", "bb")
365     assertSelect(bufferWithSegments("a", "a"), 0, options)
366     assertSelect(bufferWithSegments("a", "b"), 1, options)
367     assertSelect(bufferWithSegments("a", "c"), -1, options)
368     assertSelect(bufferWithSegments("b", "a"), 2, options)
369     assertSelect(bufferWithSegments("b", "b"), 3, options)
370     assertSelect(bufferWithSegments("b", "c"), -1, options)
371     assertSelect(bufferWithSegments("c", "a"), -1, options)
372     assertSelect(bufferWithSegments("c", "b"), -1, options)
373     assertSelect(bufferWithSegments("c", "c"), -1, options)
374     assertSelect(bufferWithSegments("a", "ad"), 0, options)
375     assertSelect(bufferWithSegments("a", "bd"), 1, options)
376     assertSelect(bufferWithSegments("a", "cd"), -1, options)
377     assertSelect(bufferWithSegments("b", "ad"), 2, options)
378     assertSelect(bufferWithSegments("b", "bd"), 3, options)
379     assertSelect(bufferWithSegments("b", "cd"), -1, options)
380     assertSelect(bufferWithSegments("c", "ad"), -1, options)
381     assertSelect(bufferWithSegments("c", "bd"), -1, options)
382     assertSelect(bufferWithSegments("c", "cd"), -1, options)
383   }
384 
utf8Optionsnull385   private fun utf8Options(vararg options: String): Options {
386     return Options.of(*options.map { it.encodeUtf8() }.toTypedArray())
387   }
388 
assertSelectnull389   private fun assertSelect(data: String, expected: Int, options: Options) {
390     assertSelect(Buffer().writeUtf8(data), expected, options)
391   }
392 
assertSelectnull393   private fun assertSelect(data: String, expected: Int, vararg options: String) {
394     assertSelect(data, expected, utf8Options(*options))
395   }
396 
assertSelectnull397   private fun assertSelect(data: Buffer, expected: Int, options: Options) {
398     val initialSize = data.size
399     val actual = data.select(options)
400 
401     assertEquals(actual, expected)
402     if (expected == -1) {
403       assertEquals(data.size, initialSize)
404     } else {
405       assertEquals(data.size + options[expected].size, initialSize)
406     }
407   }
408 
trieStringnull409   private fun Options.trieString(): String {
410     val result = StringBuilder()
411     printTrieNode(result, 0)
412     return result.toString()
413   }
414 
Optionsnull415   private fun Options.printTrieNode(out: StringBuilder, offset: Int = 0, indent: String = "") {
416     if (trie[offset + 1] != -1) {
417       // Print the prefix.
418       out.append("$indent-> ${trie[offset + 1]}\n")
419     }
420 
421     if (trie[offset] > 0) {
422       // Print the select.
423       val selectChoiceCount = trie[offset]
424       for (i in 0 until selectChoiceCount) {
425         out.append("$indent${trie[offset + 2 + i].toChar()}")
426         printTrieResult(out, trie[offset + 2 + selectChoiceCount + i], "$indent ")
427       }
428     } else {
429       // Print the scan.
430       val scanByteCount = -1 * trie[offset]
431       out.append(indent)
432       for (i in 0 until scanByteCount) {
433         out.append(trie[offset + 2 + i].toChar())
434       }
435       printTrieResult(out, trie[offset + 2 + scanByteCount], "$indent${" ".repeat(scanByteCount)}")
436     }
437   }
438 
Optionsnull439   private fun Options.printTrieResult(out: StringBuilder, result: Int, indent: String) {
440     if (result >= 0) {
441       out.append(" -> $result\n")
442     } else {
443       out.append("\n")
444       printTrieNode(out, -1 * result, indent)
445     }
446   }
447 }
448