1 /*
   2  * Copyright (c) 2014, 2016, 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.nio.Buffer;
  28 import java.nio.ByteBuffer;
  29 import java.util.ArrayList;
  30 import java.util.Collections;
  31 import java.util.Iterator;
  32 import java.util.LinkedList;
  33 import java.util.List;
  34 import java.util.function.Consumer;
  35 import java.util.function.Function;
  36 
  37 import static jdk.incubator.http.internal.hpack.BuffersTestingKit.concat;
  38 import static jdk.incubator.http.internal.hpack.BuffersTestingKit.forEachSplit;
  39 import static jdk.incubator.http.internal.hpack.SpecHelper.toHexdump;
  40 import static jdk.incubator.http.internal.hpack.TestHelper.assertVoidThrows;
  41 import static java.util.Arrays.asList;
  42 import static org.testng.Assert.assertEquals;
  43 import static org.testng.Assert.assertTrue;
  44 
  45 // TODO: map textual representation of commands from the spec to actual
  46 // calls to encoder (actually, this is a good idea for decoder as well)
  47 public final class EncoderTest {
  48 
  49     //
  50     // http://tools.ietf.org/html/rfc7541#appendix-C.2.1
  51     //
  52     @Test
  53     public void example1() {
  54 
  55         Encoder e = newCustomEncoder(256);
  56         drainInitialUpdate(e);
  57 
  58         e.literalWithIndexing("custom-key", false, "custom-header", false);
  59         // @formatter:off
  60         test(e,
  61 
  62              "400a 6375 7374 6f6d 2d6b 6579 0d63 7573\n" +
  63              "746f 6d2d 6865 6164 6572",
  64 
  65              "[  1] (s =  55) custom-key: custom-header\n" +
  66              "      Table size:  55");
  67         // @formatter:on
  68     }
  69 
  70     //
  71     // http://tools.ietf.org/html/rfc7541#appendix-C.2.2
  72     //
  73     @Test
  74     public void example2() {
  75 
  76         Encoder e = newCustomEncoder(256);
  77         drainInitialUpdate(e);
  78 
  79         e.literal(4, "/sample/path", false);
  80         // @formatter:off
  81         test(e,
  82 
  83              "040c 2f73 616d 706c 652f 7061 7468",
  84 
  85              "empty.");
  86         // @formatter:on
  87     }
  88 
  89     //
  90     // http://tools.ietf.org/html/rfc7541#appendix-C.2.3
  91     //
  92     @Test
  93     public void example3() {
  94 
  95         Encoder e = newCustomEncoder(256);
  96         drainInitialUpdate(e);
  97 
  98         e.literalNeverIndexed("password", false, "secret", false);
  99         // @formatter:off
 100         test(e,
 101 
 102              "1008 7061 7373 776f 7264 0673 6563 7265\n" +
 103              "74",
 104 
 105              "empty.");
 106         // @formatter:on
 107     }
 108 
 109     //
 110     // http://tools.ietf.org/html/rfc7541#appendix-C.2.4
 111     //
 112     @Test
 113     public void example4() {
 114 
 115         Encoder e = newCustomEncoder(256);
 116         drainInitialUpdate(e);
 117 
 118         e.indexed(2);
 119         // @formatter:off
 120         test(e,
 121 
 122              "82",
 123 
 124              "empty.");
 125         // @formatter:on
 126     }
 127 
 128     //
 129     // http://tools.ietf.org/html/rfc7541#appendix-C.3
 130     //
 131     @Test
 132     public void example5() {
 133         Encoder e = newCustomEncoder(256);
 134         drainInitialUpdate(e);
 135 
 136         ByteBuffer output = ByteBuffer.allocate(64);
 137         e.indexed(2);
 138         e.encode(output);
 139         e.indexed(6);
 140         e.encode(output);
 141         e.indexed(4);
 142         e.encode(output);
 143         e.literalWithIndexing(1, "www.example.com", false);
 144         e.encode(output);
 145 
 146         output.flip();
 147 
 148         // @formatter:off
 149         test(e, output,
 150 
 151              "8286 8441 0f77 7777 2e65 7861 6d70 6c65\n" +
 152              "2e63 6f6d",
 153 
 154              "[  1] (s =  57) :authority: www.example.com\n" +
 155              "      Table size:  57");
 156 
 157         output.clear();
 158 
 159         e.indexed( 2);
 160         e.encode(output);
 161         e.indexed( 6);
 162         e.encode(output);
 163         e.indexed( 4);
 164         e.encode(output);
 165         e.indexed(62);
 166         e.encode(output);
 167         e.literalWithIndexing(24, "no-cache", false);
 168         e.encode(output);
 169 
 170         output.flip();
 171 
 172         test(e, output,
 173 
 174              "8286 84be 5808 6e6f 2d63 6163 6865",
 175 
 176              "[  1] (s =  53) cache-control: no-cache\n" +
 177              "[  2] (s =  57) :authority: www.example.com\n" +
 178              "      Table size: 110");
 179 
 180         output.clear();
 181 
 182         e.indexed( 2);
 183         e.encode(output);
 184         e.indexed( 7);
 185         e.encode(output);
 186         e.indexed( 5);
 187         e.encode(output);
 188         e.indexed(63);
 189         e.encode(output);
 190         e.literalWithIndexing("custom-key", false, "custom-value", false);
 191         e.encode(output);
 192 
 193         output.flip();
 194 
 195         test(e, output,
 196 
 197              "8287 85bf 400a 6375 7374 6f6d 2d6b 6579\n" +
 198              "0c63 7573 746f 6d2d 7661 6c75 65",
 199 
 200              "[  1] (s =  54) custom-key: custom-value\n" +
 201              "[  2] (s =  53) cache-control: no-cache\n" +
 202              "[  3] (s =  57) :authority: www.example.com\n" +
 203              "      Table size: 164");
 204         // @formatter:on
 205     }
 206 
 207     @Test
 208     public void example5AllSplits() {
 209 
 210         List<Consumer<Encoder>> actions = new LinkedList<>();
 211         actions.add(e -> e.indexed(2));
 212         actions.add(e -> e.indexed(6));
 213         actions.add(e -> e.indexed(4));
 214         actions.add(e -> e.literalWithIndexing(1, "www.example.com", false));
 215 
 216         encodeAllSplits(
 217                 actions,
 218 
 219                 "8286 8441 0f77 7777 2e65 7861 6d70 6c65\n" +
 220                 "2e63 6f6d",
 221 
 222                 "[  1] (s =  57) :authority: www.example.com\n" +
 223                 "      Table size:  57");
 224     }
 225 
 226     private static void encodeAllSplits(Iterable<Consumer<Encoder>> consumers,
 227                                         String expectedHexdump,
 228                                         String expectedTableState) {
 229         ByteBuffer buffer = SpecHelper.toBytes(expectedHexdump);
 230         erase(buffer); // Zeroed buffer of size needed to hold the encoding
 231         forEachSplit(buffer, iterable -> {
 232             List<ByteBuffer> copy = new LinkedList<>();
 233             iterable.forEach(b -> copy.add(ByteBuffer.allocate(b.remaining())));
 234             Iterator<ByteBuffer> output = copy.iterator();
 235             if (!output.hasNext()) {
 236                 throw new IllegalStateException("No buffers to encode to");
 237             }
 238             Encoder e = newCustomEncoder(256); // FIXME: pull up (as a parameter)
 239             drainInitialUpdate(e);
 240             boolean encoded;
 241             ByteBuffer b = output.next();
 242             for (Consumer<Encoder> c : consumers) {
 243                 c.accept(e);
 244                 do {
 245                     encoded = e.encode(b);
 246                     if (!encoded) {
 247                         if (output.hasNext()) {
 248                             b = output.next();
 249                         } else {
 250                             throw new IllegalStateException("No room for encoding");
 251                         }
 252                     }
 253                 }
 254                 while (!encoded);
 255             }
 256             copy.forEach(Buffer::flip);
 257             ByteBuffer data = concat(copy);
 258             test(e, data, expectedHexdump, expectedTableState);
 259         });
 260     }
 261 
 262     //
 263     // http://tools.ietf.org/html/rfc7541#appendix-C.4
 264     //
 265     @Test
 266     public void example6() {
 267         Encoder e = newCustomEncoder(256);
 268         drainInitialUpdate(e);
 269 
 270         ByteBuffer output = ByteBuffer.allocate(64);
 271         e.indexed(2);
 272         e.encode(output);
 273         e.indexed(6);
 274         e.encode(output);
 275         e.indexed(4);
 276         e.encode(output);
 277         e.literalWithIndexing(1, "www.example.com", true);
 278         e.encode(output);
 279 
 280         output.flip();
 281 
 282         // @formatter:off
 283         test(e, output,
 284 
 285              "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4\n" +
 286              "ff",
 287 
 288              "[  1] (s =  57) :authority: www.example.com\n" +
 289              "      Table size:  57");
 290 
 291         output.clear();
 292 
 293         e.indexed( 2);
 294         e.encode(output);
 295         e.indexed( 6);
 296         e.encode(output);
 297         e.indexed( 4);
 298         e.encode(output);
 299         e.indexed(62);
 300         e.encode(output);
 301         e.literalWithIndexing(24, "no-cache", true);
 302         e.encode(output);
 303 
 304         output.flip();
 305 
 306         test(e, output,
 307 
 308              "8286 84be 5886 a8eb 1064 9cbf",
 309 
 310              "[  1] (s =  53) cache-control: no-cache\n" +
 311              "[  2] (s =  57) :authority: www.example.com\n" +
 312              "      Table size: 110");
 313 
 314         output.clear();
 315 
 316         e.indexed( 2);
 317         e.encode(output);
 318         e.indexed( 7);
 319         e.encode(output);
 320         e.indexed( 5);
 321         e.encode(output);
 322         e.indexed(63);
 323         e.encode(output);
 324         e.literalWithIndexing("custom-key", true, "custom-value", true);
 325         e.encode(output);
 326 
 327         output.flip();
 328 
 329         test(e, output,
 330 
 331              "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925\n" +
 332              "a849 e95b b8e8 b4bf",
 333 
 334              "[  1] (s =  54) custom-key: custom-value\n" +
 335              "[  2] (s =  53) cache-control: no-cache\n" +
 336              "[  3] (s =  57) :authority: www.example.com\n" +
 337              "      Table size: 164");
 338         // @formatter:on
 339     }
 340 
 341     //
 342     // http://tools.ietf.org/html/rfc7541#appendix-C.5
 343     //
 344     @Test
 345     public void example7() {
 346         Encoder e = newCustomEncoder(256);
 347         drainInitialUpdate(e);
 348 
 349         ByteBuffer output = ByteBuffer.allocate(128);
 350         // @formatter:off
 351         e.literalWithIndexing( 8, "302", false);
 352         e.encode(output);
 353         e.literalWithIndexing(24, "private", false);
 354         e.encode(output);
 355         e.literalWithIndexing(33, "Mon, 21 Oct 2013 20:13:21 GMT", false);
 356         e.encode(output);
 357         e.literalWithIndexing(46, "https://www.example.com", false);
 358         e.encode(output);
 359 
 360         output.flip();
 361 
 362         test(e, output,
 363 
 364              "4803 3330 3258 0770 7269 7661 7465 611d\n" +
 365              "4d6f 6e2c 2032 3120 4f63 7420 3230 3133\n" +
 366              "2032 303a 3133 3a32 3120 474d 546e 1768\n" +
 367              "7474 7073 3a2f 2f77 7777 2e65 7861 6d70\n" +
 368              "6c65 2e63 6f6d",
 369 
 370              "[  1] (s =  63) location: https://www.example.com\n" +
 371              "[  2] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" +
 372              "[  3] (s =  52) cache-control: private\n" +
 373              "[  4] (s =  42) :status: 302\n" +
 374              "      Table size: 222");
 375 
 376         output.clear();
 377 
 378         e.literalWithIndexing( 8, "307", false);
 379         e.encode(output);
 380         e.indexed(65);
 381         e.encode(output);
 382         e.indexed(64);
 383         e.encode(output);
 384         e.indexed(63);
 385         e.encode(output);
 386 
 387         output.flip();
 388 
 389         test(e, output,
 390 
 391              "4803 3330 37c1 c0bf",
 392 
 393              "[  1] (s =  42) :status: 307\n" +
 394              "[  2] (s =  63) location: https://www.example.com\n" +
 395              "[  3] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" +
 396              "[  4] (s =  52) cache-control: private\n" +
 397              "      Table size: 222");
 398 
 399         output.clear();
 400 
 401         e.indexed( 8);
 402         e.encode(output);
 403         e.indexed(65);
 404         e.encode(output);
 405         e.literalWithIndexing(33, "Mon, 21 Oct 2013 20:13:22 GMT", false);
 406         e.encode(output);
 407         e.indexed(64);
 408         e.encode(output);
 409         e.literalWithIndexing(26, "gzip", false);
 410         e.encode(output);
 411         e.literalWithIndexing(55, "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", false);
 412         e.encode(output);
 413 
 414         output.flip();
 415 
 416         test(e, output,
 417 
 418              "88c1 611d 4d6f 6e2c 2032 3120 4f63 7420\n" +
 419              "3230 3133 2032 303a 3133 3a32 3220 474d\n" +
 420              "54c0 5a04 677a 6970 7738 666f 6f3d 4153\n" +
 421              "444a 4b48 514b 425a 584f 5157 454f 5049\n" +
 422              "5541 5851 5745 4f49 553b 206d 6178 2d61\n" +
 423              "6765 3d33 3630 303b 2076 6572 7369 6f6e\n" +
 424              "3d31",
 425 
 426              "[  1] (s =  98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1\n" +
 427              "[  2] (s =  52) content-encoding: gzip\n" +
 428              "[  3] (s =  65) date: Mon, 21 Oct 2013 20:13:22 GMT\n" +
 429              "      Table size: 215");
 430         // @formatter:on
 431     }
 432 
 433     //
 434     // http://tools.ietf.org/html/rfc7541#appendix-C.6
 435     //
 436     @Test
 437     public void example8() {
 438         Encoder e = newCustomEncoder(256);
 439         drainInitialUpdate(e);
 440 
 441         ByteBuffer output = ByteBuffer.allocate(128);
 442         // @formatter:off
 443         e.literalWithIndexing( 8, "302", true);
 444         e.encode(output);
 445         e.literalWithIndexing(24, "private", true);
 446         e.encode(output);
 447         e.literalWithIndexing(33, "Mon, 21 Oct 2013 20:13:21 GMT", true);
 448         e.encode(output);
 449         e.literalWithIndexing(46, "https://www.example.com", true);
 450         e.encode(output);
 451 
 452         output.flip();
 453 
 454         test(e, output,
 455 
 456              "4882 6402 5885 aec3 771a 4b61 96d0 7abe\n" +
 457              "9410 54d4 44a8 2005 9504 0b81 66e0 82a6\n" +
 458              "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8\n" +
 459              "e9ae 82ae 43d3",
 460 
 461              "[  1] (s =  63) location: https://www.example.com\n" +
 462              "[  2] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" +
 463              "[  3] (s =  52) cache-control: private\n" +
 464              "[  4] (s =  42) :status: 302\n" +
 465              "      Table size: 222");
 466 
 467         output.clear();
 468 
 469         e.literalWithIndexing( 8, "307", true);
 470         e.encode(output);
 471         e.indexed(65);
 472         e.encode(output);
 473         e.indexed(64);
 474         e.encode(output);
 475         e.indexed(63);
 476         e.encode(output);
 477 
 478         output.flip();
 479 
 480         test(e, output,
 481 
 482              "4883 640e ffc1 c0bf",
 483 
 484              "[  1] (s =  42) :status: 307\n" +
 485              "[  2] (s =  63) location: https://www.example.com\n" +
 486              "[  3] (s =  65) date: Mon, 21 Oct 2013 20:13:21 GMT\n" +
 487              "[  4] (s =  52) cache-control: private\n" +
 488              "      Table size: 222");
 489 
 490         output.clear();
 491 
 492         e.indexed( 8);
 493         e.encode(output);
 494         e.indexed(65);
 495         e.encode(output);
 496         e.literalWithIndexing(33, "Mon, 21 Oct 2013 20:13:22 GMT", true);
 497         e.encode(output);
 498         e.indexed(64);
 499         e.encode(output);
 500         e.literalWithIndexing(26, "gzip", true);
 501         e.encode(output);
 502         e.literalWithIndexing(55, "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", true);
 503         e.encode(output);
 504 
 505         output.flip();
 506 
 507         test(e, output,
 508 
 509              "88c1 6196 d07a be94 1054 d444 a820 0595\n" +
 510              "040b 8166 e084 a62d 1bff c05a 839b d9ab\n" +
 511              "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b\n" +
 512              "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f\n" +
 513              "9587 3160 65c0 03ed 4ee5 b106 3d50 07",
 514 
 515              "[  1] (s =  98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1\n" +
 516              "[  2] (s =  52) content-encoding: gzip\n" +
 517              "[  3] (s =  65) date: Mon, 21 Oct 2013 20:13:22 GMT\n" +
 518              "      Table size: 215");
 519         // @formatter:on
 520     }
 521 
 522     @Test
 523     public void initialSizeUpdateDefaultEncoder() {
 524         Function<Integer, Encoder> e = Encoder::new;
 525         testSizeUpdate(e, 1024, asList(), asList(0));
 526         testSizeUpdate(e, 1024, asList(1024), asList(0));
 527         testSizeUpdate(e, 1024, asList(1024, 1024), asList(0));
 528         testSizeUpdate(e, 1024, asList(1024, 512), asList(0));
 529         testSizeUpdate(e, 1024, asList(512, 1024), asList(0));
 530         testSizeUpdate(e, 1024, asList(512, 2048), asList(0));
 531     }
 532 
 533     @Test
 534     public void initialSizeUpdateCustomEncoder() {
 535         Function<Integer, Encoder> e = EncoderTest::newCustomEncoder;
 536         testSizeUpdate(e, 1024, asList(), asList(1024));
 537         testSizeUpdate(e, 1024, asList(1024), asList(1024));
 538         testSizeUpdate(e, 1024, asList(1024, 1024), asList(1024));
 539         testSizeUpdate(e, 1024, asList(1024, 512), asList(512));
 540         testSizeUpdate(e, 1024, asList(512, 1024), asList(1024));
 541         testSizeUpdate(e, 1024, asList(512, 2048), asList(2048));
 542     }
 543 
 544     @Test
 545     public void seriesOfSizeUpdatesDefaultEncoder() {
 546         Function<Integer, Encoder> e = c -> {
 547             Encoder encoder = new Encoder(c);
 548             drainInitialUpdate(encoder);
 549             return encoder;
 550         };
 551         testSizeUpdate(e, 0, asList(0), asList());
 552         testSizeUpdate(e, 1024, asList(1024), asList());
 553         testSizeUpdate(e, 1024, asList(2048), asList());
 554         testSizeUpdate(e, 1024, asList(512), asList());
 555         testSizeUpdate(e, 1024, asList(1024, 1024), asList());
 556         testSizeUpdate(e, 1024, asList(1024, 2048), asList());
 557         testSizeUpdate(e, 1024, asList(2048, 1024), asList());
 558         testSizeUpdate(e, 1024, asList(1024, 512), asList());
 559         testSizeUpdate(e, 1024, asList(512, 1024), asList());
 560     }
 561 
 562     //
 563     // https://tools.ietf.org/html/rfc7541#section-4.2
 564     //
 565     @Test
 566     public void seriesOfSizeUpdatesCustomEncoder() {
 567         Function<Integer, Encoder> e = c -> {
 568             Encoder encoder = newCustomEncoder(c);
 569             drainInitialUpdate(encoder);
 570             return encoder;
 571         };
 572         testSizeUpdate(e, 0, asList(0), asList());
 573         testSizeUpdate(e, 1024, asList(1024), asList());
 574         testSizeUpdate(e, 1024, asList(2048), asList(2048));
 575         testSizeUpdate(e, 1024, asList(512), asList(512));
 576         testSizeUpdate(e, 1024, asList(1024, 1024), asList());
 577         testSizeUpdate(e, 1024, asList(1024, 2048), asList(2048));
 578         testSizeUpdate(e, 1024, asList(2048, 1024), asList());
 579         testSizeUpdate(e, 1024, asList(1024, 512), asList(512));
 580         testSizeUpdate(e, 1024, asList(512, 1024), asList(512, 1024));
 581     }
 582 
 583     @Test
 584     public void callSequenceViolations() {
 585         {   // Hasn't set up a header
 586             Encoder e = new Encoder(0);
 587             assertVoidThrows(IllegalStateException.class, () -> e.encode(ByteBuffer.allocate(16)));
 588         }
 589         {   // Can't set up header while there's an unfinished encoding
 590             Encoder e = new Encoder(0);
 591             e.indexed(32);
 592             assertVoidThrows(IllegalStateException.class, () -> e.indexed(32));
 593         }
 594         {   // Can't setMaxCapacity while there's an unfinished encoding
 595             Encoder e = new Encoder(0);
 596             e.indexed(32);
 597             assertVoidThrows(IllegalStateException.class, () -> e.setMaxCapacity(512));
 598         }
 599         {   // Hasn't set up a header
 600             Encoder e = new Encoder(0);
 601             e.setMaxCapacity(256);
 602             assertVoidThrows(IllegalStateException.class, () -> e.encode(ByteBuffer.allocate(16)));
 603         }
 604         {   // Hasn't set up a header after the previous encoding
 605             Encoder e = new Encoder(0);
 606             e.indexed(0);
 607             boolean encoded = e.encode(ByteBuffer.allocate(16));
 608             assertTrue(encoded); // assumption
 609             assertVoidThrows(IllegalStateException.class, () -> e.encode(ByteBuffer.allocate(16)));
 610         }
 611     }
 612 
 613     private static void test(Encoder encoder,
 614                              String expectedTableState,
 615                              String expectedHexdump) {
 616 
 617         ByteBuffer b = ByteBuffer.allocate(128);
 618         encoder.encode(b);
 619         b.flip();
 620         test(encoder, b, expectedTableState, expectedHexdump);
 621     }
 622 
 623     private static void test(Encoder encoder,
 624                              ByteBuffer output,
 625                              String expectedHexdump,
 626                              String expectedTableState) {
 627 
 628         String actualTableState = encoder.getHeaderTable().getStateString();
 629         assertEquals(actualTableState, expectedTableState);
 630 
 631         String actualHexdump = toHexdump(output);
 632         assertEquals(actualHexdump, expectedHexdump.replaceAll("\\n", " "));
 633     }
 634 
 635     // initial size - the size encoder is constructed with
 636     // updates      - a sequence of values for consecutive calls to encoder.setMaxCapacity
 637     // expected     - a sequence of values expected to be decoded by a decoder
 638     private void testSizeUpdate(Function<Integer, Encoder> encoder,
 639                                 int initialSize,
 640                                 List<Integer> updates,
 641                                 List<Integer> expected) {
 642         Encoder e = encoder.apply(initialSize);
 643         updates.forEach(e::setMaxCapacity);
 644         ByteBuffer b = ByteBuffer.allocate(64);
 645         e.header("a", "b");
 646         e.encode(b);
 647         b.flip();
 648         Decoder d = new Decoder(updates.isEmpty() ? initialSize : Collections.max(updates));
 649         List<Integer> actual = new ArrayList<>();
 650         d.decode(b, true, new DecodingCallback() {
 651             @Override
 652             public void onDecoded(CharSequence name, CharSequence value) { }
 653 
 654             @Override
 655             public void onSizeUpdate(int capacity) {
 656                 actual.add(capacity);
 657             }
 658         });
 659         assertEquals(actual, expected);
 660     }
 661 
 662     //
 663     // Default encoder does not need any table, therefore a subclass that
 664     // behaves differently is needed
 665     //
 666     private static Encoder newCustomEncoder(int maxCapacity) {
 667         return new Encoder(maxCapacity) {
 668             @Override
 669             protected int calculateCapacity(int maxCapacity) {
 670                 return maxCapacity;
 671             }
 672         };
 673     }
 674 
 675     private static void drainInitialUpdate(Encoder e) {
 676         ByteBuffer b = ByteBuffer.allocate(4);
 677         e.header("a", "b");
 678         boolean done;
 679         do {
 680             done = e.encode(b);
 681             b.flip();
 682         } while (!done);
 683     }
 684 
 685     private static void erase(ByteBuffer buffer) {
 686         buffer.clear();
 687         while (buffer.hasRemaining()) {
 688             buffer.put((byte) 0);
 689         }
 690         buffer.clear();
 691     }
 692 }