blob: a1a81f9ee49d446e1eee0e2dc51d2135ede0f4e6 [file] [log] [blame]
Sergey Mashkov86f70612017-07-25 10:59:44 +03001package kotlinx.coroutines.experimental.io
2
3import kotlinx.coroutines.experimental.*
Roman Elizarov9fe5f462018-02-21 19:05:52 +03004import org.junit.*
Roman Elizarov96d7a882017-08-30 15:17:05 +03005import org.junit.Test
Roman Elizarov9fe5f462018-02-21 19:05:52 +03006import org.junit.rules.*
Sergey Mashkov86f70612017-07-25 10:59:44 +03007import java.util.*
Roman Elizarov9fe5f462018-02-21 19:05:52 +03008import java.util.concurrent.*
9import kotlin.coroutines.experimental.*
10import kotlin.test.*
Sergey Mashkov86f70612017-07-25 10:59:44 +030011
Roman Elizarovc45f5fa2018-01-11 13:48:19 +030012class StringsTest : TestBase() {
Sergey Mashkov86f70612017-07-25 10:59:44 +030013 @get:Rule
14 val timeout = Timeout(10, TimeUnit.SECONDS)
15
16 private val channel = ByteBufferChannel(autoFlush = true)
17
18 @Test
19 fun testReadString() {
20 runBlocking {
21 writeString("Hello, World!")
22 channel.close()
23 assertEquals("Hello, World!", channel.readASCIILine())
24 }
25 }
26
27 @Test
Sergey Mashkova9a936d2017-08-23 14:39:41 +030028 fun testReadLines1() {
29 testReadLine("\r", "", "")
30 }
31
32 @Test
33 fun testReadLinesCases() {
34 testReadLine("abc", "abc", "")
35 testReadLine("", null, "")
36
37 testReadLine("\n", "", "")
38 testReadLine("\r", "", "")
39 testReadLine("\r\n", "", "")
40 testReadLine("1\n", "1", "")
41 testReadLine("1\r", "1", "")
42 testReadLine("1\r\n", "1", "")
43
44 testReadLine("\n2", "", "2")
45 testReadLine("\r2", "", "2")
46 testReadLine("\r\n2", "", "2")
47 testReadLine("1\n2", "1", "2")
48 testReadLine("1\r2", "1", "2")
49 testReadLine("1\r\n2", "1", "2")
50
51 // unicode
52 testReadLine("\u0440\n", "\u0440", "")
53 testReadLine("\u0440\n1", "\u0440", "1")
54 testReadLine("\u0440\r", "\u0440", "")
55 testReadLine("\u0440\r2", "\u0440", "2")
56 }
57
58 private fun testReadLine(source: String, expectedLine: String?, expectedRemaining: String) {
59 val content = source.toByteArray(Charsets.UTF_8)
60
61 // no splitting
62 runBlocking {
Sergey Mashkovb86a0032017-11-08 16:39:03 +030063 ByteReadChannel(content)
Sergey Mashkova9a936d2017-08-23 14:39:41 +030064 }
65
66 // split
67 for (splitAt in 0 until content.size) {
68 val ch = ByteChannel(true)
69 runBlocking {
70 launch(coroutineContext) {
71 ch.writeFully(content, 0, splitAt)
72 yield()
73 ch.writeFully(content, splitAt, content.size - splitAt)
74 ch.close()
75 }
76
77 testReadLine(ch, expectedLine, expectedRemaining)
78 }
79 }
80 }
81
82 private suspend fun testReadLine(ch: ByteReadChannel, expectedLine: String?, expectedRemaining: String) {
83 val line = ch.readUTF8Line()
84 assertEquals(expectedLine, line)
85
86 val buffer = ByteBuffer.allocate(8192)
87 val rc = ch.readAvailable(buffer)
88
89 if (expectedRemaining.isNotEmpty()) {
90 assertNotEquals(-1, rc, "Unexpected EOF. Expected >= 0")
91 }
92
93 buffer.flip()
94 assertEquals(expectedRemaining, Charsets.UTF_8.decode(buffer).toString())
95 }
96
97 @Test
Sergey Mashkov86f70612017-07-25 10:59:44 +030098 fun testReadLines() {
99 runBlocking {
100 writeString("Hello, World!\nLine2")
101 assertEquals("Hello, World!", channel.readASCIILine())
102 channel.close()
103 assertEquals("Line2", channel.readASCIILine())
104 }
105 }
106
107 @Test
108 fun testReadASCIILineLf() {
109 runBlocking {
110 writeParts("A", "B\n", "C")
111
112 assertEquals("AB", channel.readASCIILine())
113 assertEquals("C", channel.readASCIILine())
114 assertEquals(null, channel.readASCIILine())
115 }
116 }
117
118 @Test
119 fun testReadASCIILineCrLf() {
120 runBlocking {
121 writeParts("A", "B\r\n", "C")
122
123 assertEquals("AB", channel.readASCIILine())
124 assertEquals("C", channel.readASCIILine())
125 assertEquals(null, channel.readASCIILine())
126 }
127 }
128
129 @Test
130 fun testReadASCIILineCrLfBadSplit() {
131 runBlocking {
132 writeParts("A", "B\r", "\nC")
133
134 assertEquals("AB", channel.readASCIILine())
135 assertEquals("C", channel.readASCIILine())
136 assertEquals(null, channel.readASCIILine())
137 }
138 }
139
140 @Test
141 fun testReadASCIILineTrailingLf() {
142 runBlocking {
143 writeParts("A", "B\n", "C\n")
144
145 assertEquals("AB", channel.readASCIILine())
146 assertEquals("C", channel.readASCIILine())
147 assertEquals(null, channel.readASCIILine())
148 }
149 }
150
151 @Test
152 fun testReadASCIILineLeadingLf() {
153 runBlocking {
154 writeParts("\nA", "B\n", "C")
155
156 assertEquals("", channel.readASCIILine())
157 assertEquals("AB", channel.readASCIILine())
158 assertEquals("C", channel.readASCIILine())
159 assertEquals(null, channel.readASCIILine())
160 }
161 }
162
163 @Test
164 fun testLookAhead() {
165 val text = buildString() {
166 for (i in 0 until 65535) {
167 append((i and 0xf).toString(16))
168 }
169 }.toByteArray()
170
171 runBlocking {
172 launch(CommonPool) {
173 channel.writeFully(text)
174 channel.close()
175 }
176
177 val comparison = ByteBuffer.wrap(text)
178
179 val arr = ByteArray(128)
180 var rem = text.size
181 val rnd = Random()
182
183 while (rem > 0) {
184 val s = rnd.nextInt(arr.size).coerceIn(1, rem)
185 arr.fill(0)
Roman Elizarov469cad32017-08-15 15:54:56 +0300186 val rc = channel.readAvailable(arr, 0, s)
Sergey Mashkov86f70612017-07-25 10:59:44 +0300187
188 if (rc == -1) fail("EOF")
189
190 val actual = String(arr, 0, rc)
191
192 val expectedBytes = ByteArray(rc)
193 comparison.get(expectedBytes)
194 val expected = expectedBytes.toString(Charsets.ISO_8859_1)
195
196 assertEquals(expected, actual)
197
198 rem -= rc
199 }
200 }
201 }
202
203 @Test
204 fun testLongLinesConcurrent() {
205 val lines = (0..1024).map { size ->
206 buildString(size) {
207 for (i in 0 until size) {
208 append((i and 0xf).toString(16))
209 }
210 }
211 }
212
213 runBlocking {
214 launch(CommonPool) {
215 for (part in lines) {
216 writeString(part + "\n")
217 }
218 channel.close()
219 }
220
221 for (expected in lines) {
222 assertEquals(expected, channel.readASCIILine(expected.length))
223 }
224
225 assertNull(channel.readASCIILine())
226 }
227 }
228
229 @Test
230 fun testLongLinesSequential() {
231 val lines = (0..1024).map { size ->
232 buildString(size) {
233 for (i in 0 until size) {
234 append((i and 0xf).toString(16))
235 }
236 }
237 }
238
239 runBlocking {
240 launch(coroutineContext) {
241 for (part in lines) {
242 writeString(part + "\n")
243 yield()
244 }
245 channel.close()
246 }
247
248 for (expected in lines) {
249 Thread.yield()
250 assertEquals(expected, channel.readASCIILine(expected.length))
251 }
252
253 assertNull(channel.readASCIILine())
254 }
255 }
256
257 @Test
258 fun testReadUTF8Line2bytes() {
259 val parts = byteArrayOf(0xd0.toByte(), 0x9a.toByte(), 0x0a)
260
261 runBlocking {
262 channel.writeFully(parts)
263 assertEquals("\u041a", channel.readUTF8Line())
264 }
265 }
266
267 @Test
268 fun testReadUTF8Line3bytes() {
269 val parts = byteArrayOf(0xe0.toByte(), 0xaf.toByte(), 0xb5.toByte(), 0x0a)
270
271 runBlocking {
272 channel.writeFully(parts)
273 assertEquals("\u0BF5", channel.readUTF8Line())
274 }
275 }
276
277 @Test
278 fun testReadUTF8Line4bytes() {
279 val parts = byteArrayOf(0xF0.toByte(), 0xA6.toByte(), 0x88.toByte(), 0x98.toByte(), 0x0a)
280
281 runBlocking {
282 channel.writeFully(parts)
283 assertEquals("\uD858\uDE18", channel.readUTF8Line())
284 }
285 }
286
287 private suspend fun writeString(s: String) {
288 channel.writeFully(s.toByteArray(Charsets.ISO_8859_1))
289 }
290
291 private fun writeParts(vararg parts: String) {
292 launch(CommonPool) {
293 parts.forEach { p ->
294 writeString(p)
295 yield()
296 delay(1)
297 }
298
299 channel.close()
300 }
301 }
302}