|
1 /* |
|
2 * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 */ |
|
23 package jdk.internal.net.http.hpack; |
|
24 |
|
25 import org.testng.annotations.Test; |
|
26 |
|
27 import java.io.IOException; |
|
28 import java.io.UncheckedIOException; |
|
29 import java.nio.ByteBuffer; |
|
30 import java.nio.CharBuffer; |
|
31 import java.nio.charset.StandardCharsets; |
|
32 import java.util.ArrayList; |
|
33 import java.util.List; |
|
34 import java.util.Random; |
|
35 |
|
36 import static org.testng.Assert.assertEquals; |
|
37 import static org.testng.Assert.fail; |
|
38 import static jdk.internal.net.http.hpack.BuffersTestingKit.*; |
|
39 import static jdk.internal.net.http.hpack.TestHelper.newRandom; |
|
40 |
|
41 // |
|
42 // Some of the tests below overlap in what they test. This allows to diagnose |
|
43 // bugs quicker and with less pain by simply ruling out common working bits. |
|
44 // |
|
45 public final class BinaryPrimitivesTest { |
|
46 |
|
47 private final Random rnd = newRandom(); |
|
48 |
|
49 @Test |
|
50 public void integerRead1() { |
|
51 verifyRead(bytes(0b00011111, 0b10011010, 0b00001010), 1337, 5); |
|
52 } |
|
53 |
|
54 @Test |
|
55 public void integerRead2() { |
|
56 verifyRead(bytes(0b00001010), 10, 5); |
|
57 } |
|
58 |
|
59 @Test |
|
60 public void integerRead3() { |
|
61 verifyRead(bytes(0b00101010), 42, 8); |
|
62 } |
|
63 |
|
64 @Test |
|
65 public void integerWrite1() { |
|
66 verifyWrite(bytes(0b00011111, 0b10011010, 0b00001010), 1337, 5); |
|
67 } |
|
68 |
|
69 @Test |
|
70 public void integerWrite2() { |
|
71 verifyWrite(bytes(0b00001010), 10, 5); |
|
72 } |
|
73 |
|
74 @Test |
|
75 public void integerWrite3() { |
|
76 verifyWrite(bytes(0b00101010), 42, 8); |
|
77 } |
|
78 |
|
79 // |
|
80 // Since readInteger(x) is the inverse of writeInteger(x), thus: |
|
81 // |
|
82 // for all x: readInteger(writeInteger(x)) == x |
|
83 // |
|
84 @Test |
|
85 public void integerIdentity() throws IOException { |
|
86 final int MAX_VALUE = 1 << 22; |
|
87 int totalCases = 0; |
|
88 int maxFilling = 0; |
|
89 IntegerReader r = new IntegerReader(); |
|
90 IntegerWriter w = new IntegerWriter(); |
|
91 ByteBuffer buf = ByteBuffer.allocate(8); |
|
92 for (int N = 1; N < 9; N++) { |
|
93 for (int expected = 0; expected <= MAX_VALUE; expected++) { |
|
94 w.reset().configure(expected, N, 1).write(buf); |
|
95 buf.flip(); |
|
96 totalCases++; |
|
97 maxFilling = Math.max(maxFilling, buf.remaining()); |
|
98 r.reset().configure(N).read(buf); |
|
99 assertEquals(r.get(), expected); |
|
100 buf.clear(); |
|
101 } |
|
102 } |
|
103 System.out.printf("totalCases: %,d, maxFilling: %,d, maxValue: %,d%n", |
|
104 totalCases, maxFilling, MAX_VALUE); |
|
105 } |
|
106 |
|
107 @Test |
|
108 public void integerReadChunked() { |
|
109 final int NUM_TESTS = 1024; |
|
110 IntegerReader r = new IntegerReader(); |
|
111 ByteBuffer bb = ByteBuffer.allocate(8); |
|
112 IntegerWriter w = new IntegerWriter(); |
|
113 for (int i = 0; i < NUM_TESTS; i++) { |
|
114 final int N = 1 + rnd.nextInt(8); |
|
115 final int expected = rnd.nextInt(Integer.MAX_VALUE) + 1; |
|
116 w.reset().configure(expected, N, rnd.nextInt()).write(bb); |
|
117 bb.flip(); |
|
118 |
|
119 forEachSplit(bb, |
|
120 (buffers) -> { |
|
121 Iterable<? extends ByteBuffer> buf = relocateBuffers(injectEmptyBuffers(buffers)); |
|
122 r.configure(N); |
|
123 for (ByteBuffer b : buf) { |
|
124 try { |
|
125 r.read(b); |
|
126 } catch (IOException e) { |
|
127 throw new UncheckedIOException(e); |
|
128 } |
|
129 } |
|
130 assertEquals(r.get(), expected); |
|
131 r.reset(); |
|
132 }); |
|
133 bb.clear(); |
|
134 } |
|
135 } |
|
136 |
|
137 // FIXME: use maxValue in the test |
|
138 |
|
139 @Test |
|
140 // FIXME: tune values for better coverage |
|
141 public void integerWriteChunked() { |
|
142 ByteBuffer bb = ByteBuffer.allocate(6); |
|
143 IntegerWriter w = new IntegerWriter(); |
|
144 IntegerReader r = new IntegerReader(); |
|
145 for (int i = 0; i < 1024; i++) { // number of tests |
|
146 final int N = 1 + rnd.nextInt(8); |
|
147 final int payload = rnd.nextInt(255); |
|
148 final int expected = rnd.nextInt(Integer.MAX_VALUE) + 1; |
|
149 |
|
150 forEachSplit(bb, |
|
151 (buffers) -> { |
|
152 List<ByteBuffer> buf = new ArrayList<>(); |
|
153 relocateBuffers(injectEmptyBuffers(buffers)).forEach(buf::add); |
|
154 boolean written = false; |
|
155 w.configure(expected, N, payload); // TODO: test for payload it can be read after written |
|
156 for (ByteBuffer b : buf) { |
|
157 int pos = b.position(); |
|
158 written = w.write(b); |
|
159 b.position(pos); |
|
160 } |
|
161 if (!written) { |
|
162 fail("please increase bb size"); |
|
163 } |
|
164 try { |
|
165 r.configure(N).read(concat(buf)); |
|
166 } catch (IOException e) { |
|
167 throw new UncheckedIOException(e); |
|
168 } |
|
169 // TODO: check payload here |
|
170 assertEquals(r.get(), expected); |
|
171 w.reset(); |
|
172 r.reset(); |
|
173 bb.clear(); |
|
174 }); |
|
175 } |
|
176 } |
|
177 |
|
178 |
|
179 // |
|
180 // Since readString(x) is the inverse of writeString(x), thus: |
|
181 // |
|
182 // for all x: readString(writeString(x)) == x |
|
183 // |
|
184 @Test |
|
185 public void stringIdentity() throws IOException { |
|
186 final int MAX_STRING_LENGTH = 4096; |
|
187 ByteBuffer bytes = ByteBuffer.allocate(MAX_STRING_LENGTH + 6); // it takes 6 bytes to encode string length of Integer.MAX_VALUE |
|
188 CharBuffer chars = CharBuffer.allocate(MAX_STRING_LENGTH); |
|
189 StringReader reader = new StringReader(); |
|
190 StringWriter writer = new StringWriter(); |
|
191 for (int len = 0; len <= MAX_STRING_LENGTH; len++) { |
|
192 for (int i = 0; i < 64; i++) { |
|
193 // not so much "test in isolation", I know... we're testing .reset() as well |
|
194 bytes.clear(); |
|
195 chars.clear(); |
|
196 |
|
197 byte[] b = new byte[len]; |
|
198 rnd.nextBytes(b); |
|
199 |
|
200 String expected = new String(b, StandardCharsets.ISO_8859_1); // reference string |
|
201 |
|
202 boolean written = writer |
|
203 .configure(CharBuffer.wrap(expected), 0, expected.length(), false) |
|
204 .write(bytes); |
|
205 |
|
206 if (!written) { |
|
207 fail("please increase 'bytes' size"); |
|
208 } |
|
209 bytes.flip(); |
|
210 reader.read(bytes, chars); |
|
211 chars.flip(); |
|
212 assertEquals(chars.toString(), expected); |
|
213 reader.reset(); |
|
214 writer.reset(); |
|
215 } |
|
216 } |
|
217 } |
|
218 |
|
219 // @Test |
|
220 // public void huffmanStringWriteChunked() { |
|
221 // fail(); |
|
222 // } |
|
223 // |
|
224 // @Test |
|
225 // public void huffmanStringReadChunked() { |
|
226 // fail(); |
|
227 // } |
|
228 |
|
229 @Test |
|
230 public void stringWriteChunked() { |
|
231 final int MAX_STRING_LENGTH = 8; |
|
232 final ByteBuffer bytes = ByteBuffer.allocate(MAX_STRING_LENGTH + 6); |
|
233 final CharBuffer chars = CharBuffer.allocate(MAX_STRING_LENGTH); |
|
234 final StringReader reader = new StringReader(); |
|
235 final StringWriter writer = new StringWriter(); |
|
236 for (int len = 0; len <= MAX_STRING_LENGTH; len++) { |
|
237 |
|
238 byte[] b = new byte[len]; |
|
239 rnd.nextBytes(b); |
|
240 |
|
241 String expected = new String(b, StandardCharsets.ISO_8859_1); // reference string |
|
242 |
|
243 forEachSplit(bytes, (buffers) -> { |
|
244 writer.configure(expected, 0, expected.length(), false); |
|
245 boolean written = false; |
|
246 for (ByteBuffer buf : buffers) { |
|
247 int p0 = buf.position(); |
|
248 written = writer.write(buf); |
|
249 buf.position(p0); |
|
250 } |
|
251 if (!written) { |
|
252 fail("please increase 'bytes' size"); |
|
253 } |
|
254 try { |
|
255 reader.read(concat(buffers), chars); |
|
256 } catch (IOException e) { |
|
257 throw new UncheckedIOException(e); |
|
258 } |
|
259 chars.flip(); |
|
260 assertEquals(chars.toString(), expected); |
|
261 reader.reset(); |
|
262 writer.reset(); |
|
263 chars.clear(); |
|
264 bytes.clear(); |
|
265 }); |
|
266 } |
|
267 } |
|
268 |
|
269 @Test |
|
270 public void stringReadChunked() { |
|
271 final int MAX_STRING_LENGTH = 16; |
|
272 final ByteBuffer bytes = ByteBuffer.allocate(MAX_STRING_LENGTH + 6); |
|
273 final CharBuffer chars = CharBuffer.allocate(MAX_STRING_LENGTH); |
|
274 final StringReader reader = new StringReader(); |
|
275 final StringWriter writer = new StringWriter(); |
|
276 for (int len = 0; len <= MAX_STRING_LENGTH; len++) { |
|
277 |
|
278 byte[] b = new byte[len]; |
|
279 rnd.nextBytes(b); |
|
280 |
|
281 String expected = new String(b, StandardCharsets.ISO_8859_1); // reference string |
|
282 |
|
283 boolean written = writer |
|
284 .configure(CharBuffer.wrap(expected), 0, expected.length(), false) |
|
285 .write(bytes); |
|
286 writer.reset(); |
|
287 |
|
288 if (!written) { |
|
289 fail("please increase 'bytes' size"); |
|
290 } |
|
291 bytes.flip(); |
|
292 |
|
293 forEachSplit(bytes, (buffers) -> { |
|
294 for (ByteBuffer buf : buffers) { |
|
295 int p0 = buf.position(); |
|
296 try { |
|
297 reader.read(buf, chars); |
|
298 } catch (IOException e) { |
|
299 throw new UncheckedIOException(e); |
|
300 } |
|
301 buf.position(p0); |
|
302 } |
|
303 chars.flip(); |
|
304 assertEquals(chars.toString(), expected); |
|
305 reader.reset(); |
|
306 chars.clear(); |
|
307 }); |
|
308 |
|
309 bytes.clear(); |
|
310 } |
|
311 } |
|
312 |
|
313 // @Test |
|
314 // public void test_Huffman_String_Identity() { |
|
315 // StringWriter writer = new StringWriter(); |
|
316 // StringReader reader = new StringReader(); |
|
317 // // 256 * 8 gives 2048 bits in case of plain 8 bit coding |
|
318 // // 256 * 30 gives you 7680 bits or 960 bytes in case of almost |
|
319 // // improbable event of 256 30 bits symbols in a row |
|
320 // ByteBuffer binary = ByteBuffer.allocate(960); |
|
321 // CharBuffer text = CharBuffer.allocate(960 / 5); // 5 = minimum code length |
|
322 // for (int len = 0; len < 128; len++) { |
|
323 // for (int i = 0; i < 256; i++) { |
|
324 // // not so much "test in isolation", I know... |
|
325 // binary.clear(); |
|
326 // |
|
327 // byte[] bytes = new byte[len]; |
|
328 // rnd.nextBytes(bytes); |
|
329 // |
|
330 // String s = new String(bytes, StandardCharsets.ISO_8859_1); |
|
331 // |
|
332 // writer.write(CharBuffer.wrap(s), binary, true); |
|
333 // binary.flip(); |
|
334 // reader.read(binary, text); |
|
335 // text.flip(); |
|
336 // assertEquals(text.toString(), s); |
|
337 // } |
|
338 // } |
|
339 // } |
|
340 |
|
341 // TODO: atomic failures: e.g. readonly/overflow |
|
342 |
|
343 private static byte[] bytes(int... data) { |
|
344 byte[] bytes = new byte[data.length]; |
|
345 for (int i = 0; i < data.length; i++) { |
|
346 bytes[i] = (byte) data[i]; |
|
347 } |
|
348 return bytes; |
|
349 } |
|
350 |
|
351 private static void verifyRead(byte[] data, int expected, int N) { |
|
352 ByteBuffer buf = ByteBuffer.wrap(data, 0, data.length); |
|
353 IntegerReader reader = new IntegerReader(); |
|
354 try { |
|
355 reader.configure(N).read(buf); |
|
356 } catch (IOException e) { |
|
357 throw new UncheckedIOException(e); |
|
358 } |
|
359 assertEquals(expected, reader.get()); |
|
360 } |
|
361 |
|
362 private void verifyWrite(byte[] expected, int data, int N) { |
|
363 IntegerWriter w = new IntegerWriter(); |
|
364 ByteBuffer buf = ByteBuffer.allocate(2 * expected.length); |
|
365 w.configure(data, N, 1).write(buf); |
|
366 buf.flip(); |
|
367 assertEquals(ByteBuffer.wrap(expected), buf); |
|
368 } |
|
369 } |