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