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.incubator.http.internal.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.incubator.http.internal.hpack.BuffersTestingKit.*; 39 import static jdk.incubator.http.internal.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 }