Initial import.
diff --git a/test/core/transport/chttp2/hpack_parser_test.c b/test/core/transport/chttp2/hpack_parser_test.c
new file mode 100644
index 0000000..12f8b35
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_parser_test.c
@@ -0,0 +1,223 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/transport/chttp2/hpack_parser.h"
+
+#include <stdarg.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/slice.h>
+#include "test/core/util/parse_hexstring.h"
+#include "test/core/util/slice_splitter.h"
+#include "test/core/util/test_config.h"
+
+typedef struct { va_list args; } test_checker;
+
+static void onhdr(void *ud, grpc_mdelem *md) {
+  const char *ekey, *evalue;
+  test_checker *chk = ud;
+  ekey = va_arg(chk->args, char *);
+  GPR_ASSERT(ekey);
+  evalue = va_arg(chk->args, char *);
+  GPR_ASSERT(evalue);
+  GPR_ASSERT(gpr_slice_str_cmp(md->key->slice, ekey) == 0);
+  GPR_ASSERT(gpr_slice_str_cmp(md->value->slice, evalue) == 0);
+  grpc_mdelem_unref(md);
+}
+
+static void test_vector(grpc_chttp2_hpack_parser *parser,
+                        grpc_slice_split_mode mode, const char *hexstring,
+                        ... /* char *key, char *value */) {
+  gpr_slice input = parse_hexstring(hexstring);
+  gpr_slice *slices;
+  size_t nslices;
+  size_t i;
+  test_checker chk;
+
+  va_start(chk.args, hexstring);
+
+  parser->on_header = onhdr;
+  parser->on_header_user_data = &chk;
+
+  grpc_split_slices(mode, &input, 1, &slices, &nslices);
+  gpr_slice_unref(input);
+
+  for (i = 0; i < nslices; i++) {
+    GPR_ASSERT(grpc_chttp2_hpack_parser_parse(
+        parser, GPR_SLICE_START_PTR(slices[i]), GPR_SLICE_END_PTR(slices[i])));
+  }
+
+  for (i = 0; i < nslices; i++) {
+    gpr_slice_unref(slices[i]);
+  }
+  gpr_free(slices);
+
+  GPR_ASSERT(NULL == va_arg(chk.args, char *));
+
+  va_end(chk.args);
+}
+
+static void test_vectors(grpc_slice_split_mode mode) {
+  grpc_chttp2_hpack_parser parser;
+  grpc_mdctx *mdctx = grpc_mdctx_create();
+
+  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  /* D.2.1 */
+  test_vector(&parser, mode,
+              "400a 6375 7374 6f6d 2d6b 6579 0d63 7573"
+              "746f 6d2d 6865 6164 6572",
+              "custom-key", "custom-header", NULL);
+  /* D.2.2 */
+  test_vector(&parser, mode, "040c 2f73 616d 706c 652f 7061 7468", ":path",
+              "/sample/path", NULL);
+  /* D.2.3 */
+  test_vector(&parser, mode,
+              "1008 7061 7373 776f 7264 0673 6563 7265"
+              "74",
+              "password", "secret", NULL);
+  /* D.2.4 */
+  test_vector(&parser, mode, "82", ":method", "GET", NULL);
+  grpc_chttp2_hpack_parser_destroy(&parser);
+
+  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  /* D.3.1 */
+  test_vector(&parser, mode,
+              "8286 8441 0f77 7777 2e65 7861 6d70 6c65"
+              "2e63 6f6d",
+              ":method", "GET", ":scheme", "http", ":path", "/", ":authority",
+              "www.example.com", NULL);
+  /* D.3.2 */
+  test_vector(&parser, mode, "8286 84be 5808 6e6f 2d63 6163 6865", ":method",
+              "GET", ":scheme", "http", ":path", "/", ":authority",
+              "www.example.com", "cache-control", "no-cache", NULL);
+  /* D.3.3 */
+  test_vector(&parser, mode,
+              "8287 85bf 400a 6375 7374 6f6d 2d6b 6579"
+              "0c63 7573 746f 6d2d 7661 6c75 65",
+              ":method", "GET", ":scheme", "https", ":path", "/index.html",
+              ":authority", "www.example.com", "custom-key", "custom-value",
+              NULL);
+  grpc_chttp2_hpack_parser_destroy(&parser);
+
+  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  /* D.4.1 */
+  test_vector(&parser, mode,
+              "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4"
+              "ff",
+              ":method", "GET", ":scheme", "http", ":path", "/", ":authority",
+              "www.example.com", NULL);
+  /* D.4.2 */
+  test_vector(&parser, mode, "8286 84be 5886 a8eb 1064 9cbf", ":method", "GET",
+              ":scheme", "http", ":path", "/", ":authority", "www.example.com",
+              "cache-control", "no-cache", NULL);
+  /* D.4.3 */
+  test_vector(&parser, mode,
+              "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925"
+              "a849 e95b b8e8 b4bf",
+              ":method", "GET", ":scheme", "https", ":path", "/index.html",
+              ":authority", "www.example.com", "custom-key", "custom-value",
+              NULL);
+  grpc_chttp2_hpack_parser_destroy(&parser);
+
+  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  parser.table.max_bytes = 256;
+  /* D.5.1 */
+  test_vector(&parser, mode,
+              "4803 3330 3258 0770 7269 7661 7465 611d"
+              "4d6f 6e2c 2032 3120 4f63 7420 3230 3133"
+              "2032 303a 3133 3a32 3120 474d 546e 1768"
+              "7474 7073 3a2f 2f77 7777 2e65 7861 6d70"
+              "6c65 2e63 6f6d",
+              ":status", "302", "cache-control", "private", "date",
+              "Mon, 21 Oct 2013 20:13:21 GMT", "location",
+              "https://www.example.com", NULL);
+  /* D.5.2 */
+  test_vector(&parser, mode, "4803 3330 37c1 c0bf", ":status", "307",
+              "cache-control", "private", "date",
+              "Mon, 21 Oct 2013 20:13:21 GMT", "location",
+              "https://www.example.com", NULL);
+  /* D.5.3 */
+  test_vector(&parser, mode,
+              "88c1 611d 4d6f 6e2c 2032 3120 4f63 7420"
+              "3230 3133 2032 303a 3133 3a32 3220 474d"
+              "54c0 5a04 677a 6970 7738 666f 6f3d 4153"
+              "444a 4b48 514b 425a 584f 5157 454f 5049"
+              "5541 5851 5745 4f49 553b 206d 6178 2d61"
+              "6765 3d33 3630 303b 2076 6572 7369 6f6e"
+              "3d31",
+              ":status", "200", "cache-control", "private", "date",
+              "Mon, 21 Oct 2013 20:13:22 GMT", "location",
+              "https://www.example.com", "content-encoding", "gzip",
+              "set-cookie",
+              "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
+  grpc_chttp2_hpack_parser_destroy(&parser);
+
+  grpc_chttp2_hpack_parser_init(&parser, mdctx);
+  parser.table.max_bytes = 256;
+  /* D.6.1 */
+  test_vector(&parser, mode,
+              "4882 6402 5885 aec3 771a 4b61 96d0 7abe"
+              "9410 54d4 44a8 2005 9504 0b81 66e0 82a6"
+              "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8"
+              "e9ae 82ae 43d3",
+              ":status", "302", "cache-control", "private", "date",
+              "Mon, 21 Oct 2013 20:13:21 GMT", "location",
+              "https://www.example.com", NULL);
+  /* D.6.2 */
+  test_vector(&parser, mode, "4883 640e ffc1 c0bf", ":status", "307",
+              "cache-control", "private", "date",
+              "Mon, 21 Oct 2013 20:13:21 GMT", "location",
+              "https://www.example.com", NULL);
+  /* D.6.3 */
+  test_vector(&parser, mode,
+              "88c1 6196 d07a be94 1054 d444 a820 0595"
+              "040b 8166 e084 a62d 1bff c05a 839b d9ab"
+              "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b"
+              "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f"
+              "9587 3160 65c0 03ed 4ee5 b106 3d50 07",
+              ":status", "200", "cache-control", "private", "date",
+              "Mon, 21 Oct 2013 20:13:22 GMT", "location",
+              "https://www.example.com", "content-encoding", "gzip",
+              "set-cookie",
+              "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", NULL);
+  grpc_chttp2_hpack_parser_destroy(&parser);
+  grpc_mdctx_orphan(mdctx);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  test_vectors(GRPC_SLICE_SPLIT_MERGE_ALL);
+  test_vectors(GRPC_SLICE_SPLIT_ONE_BYTE);
+  return 0;
+}
diff --git a/test/core/transport/chttp2/hpack_table_test.c b/test/core/transport/chttp2/hpack_table_test.c
new file mode 100644
index 0000000..8810925
--- /dev/null
+++ b/test/core/transport/chttp2/hpack_table_test.c
@@ -0,0 +1,269 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/transport/chttp2/hpack_table.h"
+
+#include <string.h>
+#include <stdio.h>
+
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static void assert_str(const grpc_chttp2_hptbl *tbl, grpc_mdstr *mdstr,
+                       const char *str) {
+  GPR_ASSERT(gpr_slice_str_cmp(mdstr->slice, str) == 0);
+}
+
+static void assert_index(const grpc_chttp2_hptbl *tbl, int idx, const char *key,
+                         const char *value) {
+  grpc_mdelem *md = grpc_chttp2_hptbl_lookup(tbl, idx);
+  assert_str(tbl, md->key, key);
+  assert_str(tbl, md->value, value);
+}
+
+static void test_static_lookup() {
+  grpc_chttp2_hptbl tbl;
+  grpc_mdctx *mdctx;
+
+  mdctx = grpc_mdctx_create();
+  grpc_chttp2_hptbl_init(&tbl, mdctx);
+
+  LOG_TEST();
+  assert_index(&tbl, 1, ":authority", "");
+  assert_index(&tbl, 2, ":method", "GET");
+  assert_index(&tbl, 3, ":method", "POST");
+  assert_index(&tbl, 4, ":path", "/");
+  assert_index(&tbl, 5, ":path", "/index.html");
+  assert_index(&tbl, 6, ":scheme", "http");
+  assert_index(&tbl, 7, ":scheme", "https");
+  assert_index(&tbl, 8, ":status", "200");
+  assert_index(&tbl, 9, ":status", "204");
+  assert_index(&tbl, 10, ":status", "206");
+  assert_index(&tbl, 11, ":status", "304");
+  assert_index(&tbl, 12, ":status", "400");
+  assert_index(&tbl, 13, ":status", "404");
+  assert_index(&tbl, 14, ":status", "500");
+  assert_index(&tbl, 15, "accept-charset", "");
+  assert_index(&tbl, 16, "accept-encoding", "gzip, deflate");
+  assert_index(&tbl, 17, "accept-language", "");
+  assert_index(&tbl, 18, "accept-ranges", "");
+  assert_index(&tbl, 19, "accept", "");
+  assert_index(&tbl, 20, "access-control-allow-origin", "");
+  assert_index(&tbl, 21, "age", "");
+  assert_index(&tbl, 22, "allow", "");
+  assert_index(&tbl, 23, "authorization", "");
+  assert_index(&tbl, 24, "cache-control", "");
+  assert_index(&tbl, 25, "content-disposition", "");
+  assert_index(&tbl, 26, "content-encoding", "");
+  assert_index(&tbl, 27, "content-language", "");
+  assert_index(&tbl, 28, "content-length", "");
+  assert_index(&tbl, 29, "content-location", "");
+  assert_index(&tbl, 30, "content-range", "");
+  assert_index(&tbl, 31, "content-type", "");
+  assert_index(&tbl, 32, "cookie", "");
+  assert_index(&tbl, 33, "date", "");
+  assert_index(&tbl, 34, "etag", "");
+  assert_index(&tbl, 35, "expect", "");
+  assert_index(&tbl, 36, "expires", "");
+  assert_index(&tbl, 37, "from", "");
+  assert_index(&tbl, 38, "host", "");
+  assert_index(&tbl, 39, "if-match", "");
+  assert_index(&tbl, 40, "if-modified-since", "");
+  assert_index(&tbl, 41, "if-none-match", "");
+  assert_index(&tbl, 42, "if-range", "");
+  assert_index(&tbl, 43, "if-unmodified-since", "");
+  assert_index(&tbl, 44, "last-modified", "");
+  assert_index(&tbl, 45, "link", "");
+  assert_index(&tbl, 46, "location", "");
+  assert_index(&tbl, 47, "max-forwards", "");
+  assert_index(&tbl, 48, "proxy-authenticate", "");
+  assert_index(&tbl, 49, "proxy-authorization", "");
+  assert_index(&tbl, 50, "range", "");
+  assert_index(&tbl, 51, "referer", "");
+  assert_index(&tbl, 52, "refresh", "");
+  assert_index(&tbl, 53, "retry-after", "");
+  assert_index(&tbl, 54, "server", "");
+  assert_index(&tbl, 55, "set-cookie", "");
+  assert_index(&tbl, 56, "strict-transport-security", "");
+  assert_index(&tbl, 57, "transfer-encoding", "");
+  assert_index(&tbl, 58, "user-agent", "");
+  assert_index(&tbl, 59, "vary", "");
+  assert_index(&tbl, 60, "via", "");
+  assert_index(&tbl, 61, "www-authenticate", "");
+
+  grpc_chttp2_hptbl_destroy(&tbl);
+  grpc_mdctx_orphan(mdctx);
+}
+
+static void test_many_additions() {
+  grpc_chttp2_hptbl tbl;
+  int i;
+  char key[32];
+  char value[32];
+  grpc_mdctx *mdctx;
+
+  LOG_TEST();
+
+  mdctx = grpc_mdctx_create();
+  grpc_chttp2_hptbl_init(&tbl, mdctx);
+
+  for (i = 0; i < 1000000; i++) {
+    sprintf(key, "K:%d", i);
+    sprintf(value, "VALUE:%d", i);
+    grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, key, value));
+    assert_index(&tbl, 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
+    if (i) {
+      sprintf(key, "K:%d", i - 1);
+      sprintf(value, "VALUE:%d", i - 1);
+      assert_index(&tbl, 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY, key, value);
+    }
+  }
+
+  grpc_chttp2_hptbl_destroy(&tbl);
+  grpc_mdctx_orphan(mdctx);
+}
+
+static grpc_chttp2_hptbl_find_result find_simple(grpc_chttp2_hptbl *tbl,
+                                                 const char *key,
+                                                 const char *value) {
+  grpc_mdelem *md = grpc_mdelem_from_strings(tbl->mdctx, key, value);
+  grpc_chttp2_hptbl_find_result r = grpc_chttp2_hptbl_find(tbl, md);
+  grpc_mdelem_unref(md);
+  return r;
+}
+
+static void test_find() {
+  grpc_chttp2_hptbl tbl;
+  int i;
+  char buffer[32];
+  grpc_mdctx *mdctx;
+  grpc_chttp2_hptbl_find_result r;
+
+  LOG_TEST();
+
+  mdctx = grpc_mdctx_create();
+  grpc_chttp2_hptbl_init(&tbl, mdctx);
+  grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "abc", "xyz"));
+  grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "abc", "123"));
+  grpc_chttp2_hptbl_add(&tbl, grpc_mdelem_from_strings(mdctx, "x", "1"));
+
+  r = find_simple(&tbl, "abc", "123");
+  GPR_ASSERT(r.index == 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+  GPR_ASSERT(r.has_value == 1);
+
+  r = find_simple(&tbl, "abc", "xyz");
+  GPR_ASSERT(r.index == 3 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+  GPR_ASSERT(r.has_value == 1);
+
+  r = find_simple(&tbl, "x", "1");
+  GPR_ASSERT(r.index == 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+  GPR_ASSERT(r.has_value == 1);
+
+  r = find_simple(&tbl, "x", "2");
+  GPR_ASSERT(r.index == 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+  GPR_ASSERT(r.has_value == 0);
+
+  r = find_simple(&tbl, "vary", "some-vary-arg");
+  GPR_ASSERT(r.index == 59);
+  GPR_ASSERT(r.has_value == 0);
+
+  r = find_simple(&tbl, "accept-encoding", "gzip, deflate");
+  GPR_ASSERT(r.index == 16);
+  GPR_ASSERT(r.has_value == 1);
+
+  r = find_simple(&tbl, "accept-encoding", "gzip");
+  GPR_ASSERT(r.index == 16);
+  GPR_ASSERT(r.has_value == 0);
+
+  r = find_simple(&tbl, ":method", "GET");
+  GPR_ASSERT(r.index == 2);
+  GPR_ASSERT(r.has_value == 1);
+
+  r = find_simple(&tbl, ":method", "POST");
+  GPR_ASSERT(r.index == 3);
+  GPR_ASSERT(r.has_value == 1);
+
+  r = find_simple(&tbl, ":method", "PUT");
+  GPR_ASSERT(r.index == 2 || r.index == 3);
+  GPR_ASSERT(r.has_value == 0);
+
+  r = find_simple(&tbl, "this-does-not-exist", "");
+  GPR_ASSERT(r.index == 0);
+  GPR_ASSERT(r.has_value == 0);
+
+  /* overflow the string buffer, check find still works */
+  for (i = 0; i < 10000; i++) {
+    sprintf(buffer, "%d", i);
+    grpc_chttp2_hptbl_add(&tbl,
+                          grpc_mdelem_from_strings(mdctx, "test", buffer));
+  }
+
+  r = find_simple(&tbl, "abc", "123");
+  GPR_ASSERT(r.index == 0);
+  GPR_ASSERT(r.has_value == 0);
+
+  r = find_simple(&tbl, "test", "9999");
+  GPR_ASSERT(r.index == 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+  GPR_ASSERT(r.has_value == 1);
+
+  r = find_simple(&tbl, "test", "9998");
+  GPR_ASSERT(r.index == 2 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+  GPR_ASSERT(r.has_value == 1);
+
+  for (i = 0; i < tbl.num_ents; i++) {
+    int expect = 9999 - i;
+    sprintf(buffer, "%d", expect);
+
+    r = find_simple(&tbl, "test", buffer);
+    GPR_ASSERT(r.index == i + 1 + GRPC_CHTTP2_LAST_STATIC_ENTRY);
+    GPR_ASSERT(r.has_value == 1);
+  }
+
+  r = find_simple(&tbl, "test", "10000");
+  GPR_ASSERT(r.index != 0);
+  GPR_ASSERT(r.has_value == 0);
+
+  grpc_chttp2_hptbl_destroy(&tbl);
+  grpc_mdctx_orphan(mdctx);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  test_static_lookup();
+  test_many_additions();
+  test_find();
+  return 0;
+}
diff --git a/test/core/transport/chttp2/status_conversion_test.c b/test/core/transport/chttp2/status_conversion_test.c
new file mode 100644
index 0000000..bb5d7b8
--- /dev/null
+++ b/test/core/transport/chttp2/status_conversion_test.c
@@ -0,0 +1,138 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/transport/chttp2/status_conversion.h"
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#define GRPC_STATUS_TO_HTTP2_ERROR(a, b) \
+  GPR_ASSERT(grpc_chttp2_grpc_status_to_http2_error(a) == (b))
+#define HTTP2_ERROR_TO_GRPC_STATUS(a, b) \
+  GPR_ASSERT(grpc_chttp2_http2_error_to_grpc_status(a) == (b))
+#define GRPC_STATUS_TO_HTTP2_STATUS(a, b) \
+  GPR_ASSERT(grpc_chttp2_grpc_status_to_http2_status(a) == (b))
+#define HTTP2_STATUS_TO_GRPC_STATUS(a, b) \
+  GPR_ASSERT(grpc_chttp2_http2_status_to_grpc_status(a) == (b))
+
+int main(int argc, char **argv) {
+  int i;
+
+  grpc_test_init(argc, argv);
+
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_OK, GRPC_CHTTP2_NO_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_CANCELLED, GRPC_CHTTP2_CANCEL);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNKNOWN, GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_INVALID_ARGUMENT,
+                             GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_DEADLINE_EXCEEDED,
+                             GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_NOT_FOUND, GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_ALREADY_EXISTS,
+                             GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_PERMISSION_DENIED,
+                             GRPC_CHTTP2_INADEQUATE_SECURITY);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNAUTHENTICATED,
+                             GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_RESOURCE_EXHAUSTED,
+                             GRPC_CHTTP2_ENHANCE_YOUR_CALM);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_FAILED_PRECONDITION,
+                             GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_ABORTED, GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_OUT_OF_RANGE,
+                             GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNIMPLEMENTED,
+                             GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_INTERNAL, GRPC_CHTTP2_INTERNAL_ERROR);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_UNAVAILABLE,
+                             GRPC_CHTTP2_REFUSED_STREAM);
+  GRPC_STATUS_TO_HTTP2_ERROR(GRPC_STATUS_DATA_LOSS, GRPC_CHTTP2_INTERNAL_ERROR);
+
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_OK, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_CANCELLED, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_UNKNOWN, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_INVALID_ARGUMENT, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_DEADLINE_EXCEEDED, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_NOT_FOUND, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_ALREADY_EXISTS, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_PERMISSION_DENIED, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_UNAUTHENTICATED, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_RESOURCE_EXHAUSTED, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_FAILED_PRECONDITION, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_ABORTED, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_OUT_OF_RANGE, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_UNIMPLEMENTED, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_INTERNAL, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_UNAVAILABLE, 200);
+  GRPC_STATUS_TO_HTTP2_STATUS(GRPC_STATUS_DATA_LOSS, 200);
+
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_NO_ERROR, GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_PROTOCOL_ERROR, GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INTERNAL_ERROR, GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FLOW_CONTROL_ERROR,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_SETTINGS_TIMEOUT,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_STREAM_CLOSED, GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_FRAME_SIZE_ERROR,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_REFUSED_STREAM,
+                             GRPC_STATUS_UNAVAILABLE);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CANCEL, GRPC_STATUS_CANCELLED);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_COMPRESSION_ERROR,
+                             GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_CONNECT_ERROR, GRPC_STATUS_INTERNAL);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_ENHANCE_YOUR_CALM,
+                             GRPC_STATUS_RESOURCE_EXHAUSTED);
+  HTTP2_ERROR_TO_GRPC_STATUS(GRPC_CHTTP2_INADEQUATE_SECURITY,
+                             GRPC_STATUS_PERMISSION_DENIED);
+
+  HTTP2_STATUS_TO_GRPC_STATUS(200, GRPC_STATUS_OK);
+  HTTP2_STATUS_TO_GRPC_STATUS(400, GRPC_STATUS_INVALID_ARGUMENT);
+  HTTP2_STATUS_TO_GRPC_STATUS(401, GRPC_STATUS_UNAUTHENTICATED);
+  HTTP2_STATUS_TO_GRPC_STATUS(403, GRPC_STATUS_PERMISSION_DENIED);
+  HTTP2_STATUS_TO_GRPC_STATUS(404, GRPC_STATUS_NOT_FOUND);
+  HTTP2_STATUS_TO_GRPC_STATUS(409, GRPC_STATUS_ABORTED);
+  HTTP2_STATUS_TO_GRPC_STATUS(412, GRPC_STATUS_FAILED_PRECONDITION);
+  HTTP2_STATUS_TO_GRPC_STATUS(429, GRPC_STATUS_RESOURCE_EXHAUSTED);
+  HTTP2_STATUS_TO_GRPC_STATUS(499, GRPC_STATUS_CANCELLED);
+  HTTP2_STATUS_TO_GRPC_STATUS(500, GRPC_STATUS_UNKNOWN);
+  HTTP2_STATUS_TO_GRPC_STATUS(503, GRPC_STATUS_UNAVAILABLE);
+  HTTP2_STATUS_TO_GRPC_STATUS(504, GRPC_STATUS_DEADLINE_EXCEEDED);
+
+  /* check all status values can be converted */
+  for (i = 0; i <= 999; i++) {
+    grpc_chttp2_http2_status_to_grpc_status(i);
+  }
+
+  return 0;
+}
diff --git a/test/core/transport/chttp2/stream_encoder_test.c b/test/core/transport/chttp2/stream_encoder_test.c
new file mode 100644
index 0000000..3ee11d9
--- /dev/null
+++ b/test/core/transport/chttp2/stream_encoder_test.c
@@ -0,0 +1,320 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/transport/chttp2/stream_encoder.h"
+
+#include <stdio.h>
+
+#include "src/core/transport/chttp2/hpack_parser.h"
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string.h>
+#include "test/core/util/parse_hexstring.h"
+#include "test/core/util/slice_splitter.h"
+#include "test/core/util/test_config.h"
+
+#define TEST(x) run_test(x, #x)
+
+grpc_mdctx *g_mdctx;
+grpc_chttp2_hpack_compressor g_compressor;
+int g_failure = 0;
+grpc_stream_op_buffer g_sopb;
+
+static gpr_slice create_test_slice(size_t length) {
+  gpr_slice slice = gpr_slice_malloc(length);
+  size_t i;
+  for (i = 0; i < length; i++) {
+    GPR_SLICE_START_PTR(slice)[i] = i;
+  }
+  return slice;
+}
+
+/* verify that the output generated by encoding the stream matches the
+   hexstring passed in */
+static void verify_sopb(size_t window_available, int eof,
+                        size_t expect_window_used, const char *expected) {
+  gpr_slice_buffer output;
+  gpr_slice merged;
+  gpr_slice expect = parse_hexstring(expected);
+  gpr_slice_buffer_init(&output);
+  GPR_ASSERT(expect_window_used ==
+             grpc_chttp2_encode_some(g_sopb.ops, &g_sopb.nops, eof, &output,
+                                     window_available, 0xdeadbeef,
+                                     &g_compressor));
+  merged = grpc_slice_merge(output.slices, output.count);
+  gpr_slice_buffer_destroy(&output);
+
+  if (0 != gpr_slice_cmp(merged, expect)) {
+    char *expect_str =
+        gpr_hexdump((char *)GPR_SLICE_START_PTR(expect),
+                    GPR_SLICE_LENGTH(expect), GPR_HEXDUMP_PLAINTEXT);
+    char *got_str =
+        gpr_hexdump((char *)GPR_SLICE_START_PTR(merged),
+                    GPR_SLICE_LENGTH(merged), GPR_HEXDUMP_PLAINTEXT);
+    gpr_log(GPR_ERROR, "mismatched output for %s", expected);
+    gpr_log(GPR_ERROR, "EXPECT: %s", expect_str);
+    gpr_log(GPR_ERROR, "GOT:    %s", got_str);
+    gpr_free(expect_str);
+    gpr_free(got_str);
+    g_failure = 1;
+  }
+
+  gpr_slice_unref(merged);
+  gpr_slice_unref(expect);
+}
+
+static void assert_result_ok(void *user_data, grpc_op_error error) {
+  GPR_ASSERT(error == GRPC_OP_OK);
+}
+
+static void test_small_data_framing() {
+  grpc_sopb_add_no_op(&g_sopb);
+  verify_sopb(10, 0, 0, "");
+
+  grpc_sopb_add_flow_ctl_cb(&g_sopb, assert_result_ok, NULL);
+  grpc_sopb_add_slice(&g_sopb, create_test_slice(3));
+  verify_sopb(10, 0, 3, "000003 0000 deadbeef 000102");
+
+  grpc_sopb_add_slice(&g_sopb, create_test_slice(4));
+  verify_sopb(10, 0, 4, "000004 0000 deadbeef 00010203");
+
+  grpc_sopb_add_slice(&g_sopb, create_test_slice(3));
+  grpc_sopb_add_slice(&g_sopb, create_test_slice(4));
+  verify_sopb(10, 0, 7, "000007 0000 deadbeef 000102 00010203");
+
+  grpc_sopb_add_slice(&g_sopb, create_test_slice(0));
+  grpc_sopb_add_slice(&g_sopb, create_test_slice(0));
+  grpc_sopb_add_slice(&g_sopb, create_test_slice(0));
+  grpc_sopb_add_slice(&g_sopb, create_test_slice(0));
+  grpc_sopb_add_slice(&g_sopb, create_test_slice(3));
+  verify_sopb(10, 0, 3, "000003 0000 deadbeef 000102");
+
+  verify_sopb(10, 1, 0, "000000 0001 deadbeef");
+
+  grpc_sopb_add_begin_message(&g_sopb, 255, 0);
+  verify_sopb(10, 0, 5, "000005 0000 deadbeef 00000000ff");
+}
+
+static void add_sopb_header(const char *key, const char *value) {
+  grpc_sopb_add_metadata(&g_sopb,
+                         grpc_mdelem_from_strings(g_mdctx, key, value));
+}
+
+static void test_basic_headers() {
+  int i;
+
+  add_sopb_header("a", "a");
+  verify_sopb(0, 0, 0, "000005 0104 deadbeef 40 0161 0161");
+
+  add_sopb_header("a", "a");
+  verify_sopb(0, 0, 0, "000001 0104 deadbeef be");
+
+  add_sopb_header("a", "a");
+  verify_sopb(0, 0, 0, "000001 0104 deadbeef be");
+
+  add_sopb_header("a", "a");
+  add_sopb_header("b", "c");
+  verify_sopb(0, 0, 0, "000006 0104 deadbeef be 40 0162 0163");
+
+  add_sopb_header("a", "a");
+  add_sopb_header("b", "c");
+  verify_sopb(0, 0, 0, "000002 0104 deadbeef bf be");
+
+  add_sopb_header("a", "d");
+  verify_sopb(0, 0, 0, "000004 0104 deadbeef 7f 00 0164");
+
+  /* flush out what's there to make a few values look very popular */
+  for (i = 0; i < 350; i++) {
+    add_sopb_header("a", "a");
+    add_sopb_header("b", "c");
+    add_sopb_header("a", "d");
+    verify_sopb(0, 0, 0, "000003 0104 deadbeef c0 bf be");
+  }
+
+  add_sopb_header("a", "a");
+  add_sopb_header("k", "v");
+  verify_sopb(0, 0, 0, "000006 0104 deadbeef c0 00 016b 0176");
+
+  add_sopb_header("a", "v");
+  /* this could be      000004 0104 deadbeef 0f 30 0176 also */
+  verify_sopb(0, 0, 0, "000004 0104 deadbeef 0f 2f 0176");
+}
+
+static void encode_int_to_str(int i, char *p) {
+  p[0] = 'a' + i % 26;
+  i /= 26;
+  GPR_ASSERT(i < 26);
+  p[1] = 'a' + i;
+  p[2] = 0;
+}
+
+static void test_decode_table_overflow() {
+  int i;
+  char key[3], value[3];
+  char expect[128];
+
+  for (i = 0; i < 114; i++) {
+    if (i > 0) {
+      add_sopb_header("aa", "ba");
+    }
+
+    encode_int_to_str(i, key);
+    encode_int_to_str(i + 1, value);
+
+    if (i + 61 >= 127) {
+      sprintf(expect, "000009 0104 deadbeef ff%02x 40 02%02x%02x 02%02x%02x",
+              i + 61 - 127, key[0], key[1], value[0], value[1]);
+    } else if (i > 0) {
+      sprintf(expect, "000008 0104 deadbeef %02x 40 02%02x%02x 02%02x%02x",
+              0x80 + 61 + i, key[0], key[1], value[0], value[1]);
+    } else {
+      sprintf(expect, "000007 0104 deadbeef 40 02%02x%02x 02%02x%02x", key[0],
+              key[1], value[0], value[1]);
+    }
+
+    add_sopb_header(key, value);
+    verify_sopb(0, 0, 0, expect);
+  }
+
+  /* if the above passes, then we must have just knocked this pair out of the
+     decoder stack, and so we'll be forced to re-encode it */
+  add_sopb_header("aa", "ba");
+  verify_sopb(0, 0, 0, "000007 0104 deadbeef 40 026161 026261");
+}
+
+static void randstr(char *p, int bufsz) {
+  int i;
+  int len = 1 + rand() % bufsz;
+  for (i = 0; i < len; i++) {
+    p[i] = 'a' + rand() % 26;
+  }
+  p[len] = 0;
+}
+
+typedef struct {
+  char key[300];
+  char value[300];
+  int got_hdr;
+} test_decode_random_header_state;
+
+static void chk_hdr(void *p, grpc_mdelem *el) {
+  test_decode_random_header_state *st = p;
+  GPR_ASSERT(0 == gpr_slice_str_cmp(el->key->slice, st->key));
+  GPR_ASSERT(0 == gpr_slice_str_cmp(el->value->slice, st->value));
+  st->got_hdr = 1;
+  grpc_mdelem_unref(el);
+}
+
+static void test_decode_random_headers_inner(int max_len) {
+  int i;
+  test_decode_random_header_state st;
+  gpr_slice_buffer output;
+  gpr_slice merged;
+  grpc_chttp2_hpack_parser parser;
+
+  grpc_chttp2_hpack_parser_init(&parser, g_mdctx);
+
+  gpr_log(GPR_INFO, "max_len = %d", max_len);
+
+  for (i = 0; i < 100000; i++) {
+    randstr(st.key, max_len);
+    randstr(st.value, max_len);
+
+    add_sopb_header(st.key, st.value);
+    gpr_slice_buffer_init(&output);
+    GPR_ASSERT(0 == grpc_chttp2_encode_some(g_sopb.ops, &g_sopb.nops, 0,
+                                            &output, 0, 0xdeadbeef,
+                                            &g_compressor));
+    merged = grpc_slice_merge(output.slices, output.count);
+    gpr_slice_buffer_destroy(&output);
+
+    st.got_hdr = 0;
+    parser.on_header = chk_hdr;
+    parser.on_header_user_data = &st;
+    grpc_chttp2_hpack_parser_parse(&parser, GPR_SLICE_START_PTR(merged) + 9,
+                                   GPR_SLICE_END_PTR(merged));
+    GPR_ASSERT(st.got_hdr);
+
+    gpr_slice_unref(merged);
+  }
+
+  grpc_chttp2_hpack_parser_destroy(&parser);
+}
+
+#define DECL_TEST_DECODE_RANDOM_HEADERS(n)       \
+  static void test_decode_random_headers_##n() { \
+    test_decode_random_headers_inner(n);         \
+  }                                              \
+  int keeps_formatting_correct_##n
+
+DECL_TEST_DECODE_RANDOM_HEADERS(1);
+DECL_TEST_DECODE_RANDOM_HEADERS(2);
+DECL_TEST_DECODE_RANDOM_HEADERS(3);
+DECL_TEST_DECODE_RANDOM_HEADERS(5);
+DECL_TEST_DECODE_RANDOM_HEADERS(8);
+DECL_TEST_DECODE_RANDOM_HEADERS(13);
+DECL_TEST_DECODE_RANDOM_HEADERS(21);
+DECL_TEST_DECODE_RANDOM_HEADERS(34);
+DECL_TEST_DECODE_RANDOM_HEADERS(55);
+DECL_TEST_DECODE_RANDOM_HEADERS(89);
+DECL_TEST_DECODE_RANDOM_HEADERS(144);
+
+static void run_test(void (*test)(), const char *name) {
+  gpr_log(GPR_INFO, "RUN TEST: %s", name);
+  g_mdctx = grpc_mdctx_create_with_seed(0);
+  grpc_chttp2_hpack_compressor_init(&g_compressor, g_mdctx);
+  grpc_sopb_init(&g_sopb);
+  test();
+  grpc_chttp2_hpack_compressor_destroy(&g_compressor);
+  grpc_mdctx_orphan(g_mdctx);
+  grpc_sopb_destroy(&g_sopb);
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  TEST(test_small_data_framing);
+  TEST(test_basic_headers);
+  TEST(test_decode_table_overflow);
+  TEST(test_decode_random_headers_1);
+  TEST(test_decode_random_headers_2);
+  TEST(test_decode_random_headers_3);
+  TEST(test_decode_random_headers_5);
+  TEST(test_decode_random_headers_8);
+  TEST(test_decode_random_headers_13);
+  TEST(test_decode_random_headers_21);
+  TEST(test_decode_random_headers_34);
+  TEST(test_decode_random_headers_55);
+  TEST(test_decode_random_headers_89);
+  TEST(test_decode_random_headers_144);
+  return g_failure;
+}
diff --git a/test/core/transport/chttp2/stream_map_test.c b/test/core/transport/chttp2/stream_map_test.c
new file mode 100644
index 0000000..459ef2a
--- /dev/null
+++ b/test/core/transport/chttp2/stream_map_test.c
@@ -0,0 +1,228 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/transport/chttp2/stream_map.h"
+#include <grpc/support/log.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+/* test creation & destruction */
+static void test_no_op() {
+  grpc_chttp2_stream_map map;
+
+  LOG_TEST();
+
+  grpc_chttp2_stream_map_init(&map, 8);
+  grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* test lookup on an empty map */
+static void test_empty_find() {
+  grpc_chttp2_stream_map map;
+
+  LOG_TEST();
+
+  grpc_chttp2_stream_map_init(&map, 8);
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 39128));
+  grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* test it's safe to delete twice */
+static void test_double_deletion() {
+  grpc_chttp2_stream_map map;
+
+  LOG_TEST();
+
+  grpc_chttp2_stream_map_init(&map, 8);
+  GPR_ASSERT(0 == grpc_chttp2_stream_map_size(&map));
+  grpc_chttp2_stream_map_add(&map, 1, (void *)1);
+  GPR_ASSERT((void *)1 == grpc_chttp2_stream_map_find(&map, 1));
+  GPR_ASSERT(1 == grpc_chttp2_stream_map_size(&map));
+  GPR_ASSERT((void *)1 == grpc_chttp2_stream_map_delete(&map, 1));
+  GPR_ASSERT(0 == grpc_chttp2_stream_map_size(&map));
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 1));
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_delete(&map, 1));
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 1));
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_delete(&map, 1));
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 1));
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_delete(&map, 1));
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 1));
+  grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* test add & lookup */
+static void test_basic_add_find(size_t n) {
+  grpc_chttp2_stream_map map;
+  size_t i;
+  size_t got;
+
+  LOG_TEST();
+  gpr_log(GPR_INFO, "n = %d", n);
+
+  grpc_chttp2_stream_map_init(&map, 8);
+  GPR_ASSERT(0 == grpc_chttp2_stream_map_size(&map));
+  for (i = 1; i <= n; i++) {
+    grpc_chttp2_stream_map_add(&map, i, (void *)(gpr_uintptr)i);
+  }
+  GPR_ASSERT(n == grpc_chttp2_stream_map_size(&map));
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, 0));
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(&map, n + 1));
+  for (i = 1; i <= n; i++) {
+    got = (gpr_uintptr)grpc_chttp2_stream_map_find(&map, i);
+    GPR_ASSERT(i == got);
+  }
+  grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* verify that for_each gets the right values during test_delete_evens_XXX */
+static void verify_for_each(void *user_data, gpr_uint32 stream_id, void *ptr) {
+  size_t *for_each_check = user_data;
+  GPR_ASSERT(ptr);
+  GPR_ASSERT(*for_each_check == stream_id);
+  *for_each_check += 2;
+}
+
+static void check_delete_evens(grpc_chttp2_stream_map *map, size_t n) {
+  size_t for_each_check = 1;
+  size_t i;
+  size_t got;
+
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(map, 0));
+  GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(map, n + 1));
+  for (i = 1; i <= n; i++) {
+    if (i & 1) {
+      got = (gpr_uintptr)grpc_chttp2_stream_map_find(map, i);
+      GPR_ASSERT(i == got);
+    } else {
+      GPR_ASSERT(NULL == grpc_chttp2_stream_map_find(map, i));
+    }
+  }
+
+  grpc_chttp2_stream_map_for_each(map, verify_for_each, &for_each_check);
+  if (n & 1) {
+    GPR_ASSERT(for_each_check == n + 2);
+  } else {
+    GPR_ASSERT(for_each_check == n + 1);
+  }
+}
+
+/* add a bunch of keys, delete the even ones, and make sure the map is
+   consistent */
+static void test_delete_evens_sweep(size_t n) {
+  grpc_chttp2_stream_map map;
+  size_t i;
+
+  LOG_TEST();
+  gpr_log(GPR_INFO, "n = %d", n);
+
+  grpc_chttp2_stream_map_init(&map, 8);
+  for (i = 1; i <= n; i++) {
+    grpc_chttp2_stream_map_add(&map, i, (void *)(gpr_uintptr)i);
+  }
+  for (i = 1; i <= n; i++) {
+    if ((i & 1) == 0) {
+      GPR_ASSERT((void *)i == grpc_chttp2_stream_map_delete(&map, i));
+    }
+  }
+  check_delete_evens(&map, n);
+  grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* add a bunch of keys, delete the even ones immediately, and make sure the map
+   is consistent */
+static void test_delete_evens_incremental(size_t n) {
+  grpc_chttp2_stream_map map;
+  size_t i;
+
+  LOG_TEST();
+  gpr_log(GPR_INFO, "n = %d", n);
+
+  grpc_chttp2_stream_map_init(&map, 8);
+  for (i = 1; i <= n; i++) {
+    grpc_chttp2_stream_map_add(&map, i, (void *)(gpr_uintptr)i);
+    if ((i & 1) == 0) {
+      grpc_chttp2_stream_map_delete(&map, i);
+    }
+  }
+  check_delete_evens(&map, n);
+  grpc_chttp2_stream_map_destroy(&map);
+}
+
+/* add a bunch of keys, delete old ones after some time, ensure the
+   backing array does not grow */
+static void test_periodic_compaction(size_t n) {
+  grpc_chttp2_stream_map map;
+  size_t i;
+  size_t del;
+
+  LOG_TEST();
+  gpr_log(GPR_INFO, "n = %d", n);
+
+  grpc_chttp2_stream_map_init(&map, 16);
+  GPR_ASSERT(map.capacity == 16);
+  for (i = 1; i <= n; i++) {
+    grpc_chttp2_stream_map_add(&map, i, (void *)i);
+    if (i > 8) {
+      del = i - 8;
+      GPR_ASSERT((void *)del == grpc_chttp2_stream_map_delete(&map, del));
+    }
+  }
+  GPR_ASSERT(map.capacity == 16);
+  grpc_chttp2_stream_map_destroy(&map);
+}
+
+int main(int argc, char **argv) {
+  int n = 1;
+  int prev = 1;
+  int tmp;
+
+  grpc_test_init(argc, argv);
+
+  test_no_op();
+  test_empty_find();
+  test_double_deletion();
+
+  while (n < 10000000) {
+    test_basic_add_find(n);
+    test_delete_evens_sweep(n);
+    test_delete_evens_incremental(n);
+    test_periodic_compaction(n);
+
+    tmp = n;
+    n += prev;
+    prev = tmp;
+  }
+
+  return 0;
+}
diff --git a/test/core/transport/chttp2/timeout_encoding_test.c b/test/core/transport/chttp2/timeout_encoding_test.c
new file mode 100644
index 0000000..793d2b9
--- /dev/null
+++ b/test/core/transport/chttp2/timeout_encoding_test.c
@@ -0,0 +1,140 @@
+/*
+ *
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "src/core/transport/chttp2/timeout_encoding.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <grpc/support/log.h>
+#include <grpc/support/useful.h>
+#include "test/core/util/test_config.h"
+
+#define LOG_TEST() gpr_log(GPR_INFO, "%s", __FUNCTION__)
+
+static void assert_encodes_as(gpr_timespec ts, const char *s) {
+  char buffer[32];
+  grpc_chttp2_encode_timeout(ts, buffer);
+  gpr_log(GPR_INFO, "check '%s' == '%s'", buffer, s);
+  GPR_ASSERT(0 == strcmp(buffer, s));
+}
+
+void test_encoding() {
+  LOG_TEST();
+  assert_encodes_as(gpr_time_from_micros(-1), "1n");
+  assert_encodes_as(gpr_time_from_seconds(-10), "1n");
+  assert_encodes_as(gpr_time_from_nanos(10), "10n");
+  assert_encodes_as(gpr_time_from_nanos(999999999), "1S");
+  assert_encodes_as(gpr_time_from_micros(1), "1u");
+  assert_encodes_as(gpr_time_from_micros(10), "10u");
+  assert_encodes_as(gpr_time_from_micros(100), "100u");
+  assert_encodes_as(gpr_time_from_micros(890), "890u");
+  assert_encodes_as(gpr_time_from_micros(900), "900u");
+  assert_encodes_as(gpr_time_from_micros(901), "901u");
+  assert_encodes_as(gpr_time_from_millis(1), "1m");
+  assert_encodes_as(gpr_time_from_millis(2), "2m");
+  assert_encodes_as(gpr_time_from_micros(10001), "10100u");
+  assert_encodes_as(gpr_time_from_micros(999999), "1S");
+  assert_encodes_as(gpr_time_from_millis(1000), "1S");
+  assert_encodes_as(gpr_time_from_millis(2000), "2S");
+  assert_encodes_as(gpr_time_from_millis(2500), "2500m");
+  assert_encodes_as(gpr_time_from_millis(59900), "59900m");
+  assert_encodes_as(gpr_time_from_seconds(50), "50S");
+  assert_encodes_as(gpr_time_from_seconds(59), "59S");
+  assert_encodes_as(gpr_time_from_seconds(60), "1M");
+  assert_encodes_as(gpr_time_from_seconds(80), "80S");
+  assert_encodes_as(gpr_time_from_seconds(90), "90S");
+  assert_encodes_as(gpr_time_from_minutes(2), "2M");
+  assert_encodes_as(gpr_time_from_minutes(20), "20M");
+  assert_encodes_as(gpr_time_from_hours(1), "1H");
+  assert_encodes_as(gpr_time_from_hours(10), "10H");
+  assert_encodes_as(gpr_time_from_seconds(1000000000), "1000000000S");
+}
+
+static void assert_decodes_as(const char *buffer, gpr_timespec expected) {
+  gpr_timespec got;
+  gpr_log(GPR_INFO, "check decoding '%s'", buffer);
+  GPR_ASSERT(1 == grpc_chttp2_decode_timeout(buffer, &got));
+  GPR_ASSERT(0 == gpr_time_cmp(got, expected));
+}
+
+void decode_suite(char ext, gpr_timespec (*answer)(long x)) {
+  long test_vals[] = {1,       12,       123,       1234,     12345,   123456,
+                      1234567, 12345678, 123456789, 98765432, 9876543, 987654,
+                      98765,   9876,     987,       98,       9};
+  int i;
+  char input[32];
+  for (i = 0; i < GPR_ARRAY_SIZE(test_vals); i++) {
+    sprintf(input, "%ld%c", test_vals[i], ext);
+    assert_decodes_as(input, answer(test_vals[i]));
+    sprintf(input, "   %ld%c", test_vals[i], ext);
+    assert_decodes_as(input, answer(test_vals[i]));
+    sprintf(input, "%ld %c", test_vals[i], ext);
+    assert_decodes_as(input, answer(test_vals[i]));
+    sprintf(input, "%ld %c  ", test_vals[i], ext);
+    assert_decodes_as(input, answer(test_vals[i]));
+  }
+}
+
+void test_decoding() {
+  LOG_TEST();
+  decode_suite('n', gpr_time_from_nanos);
+  decode_suite('u', gpr_time_from_micros);
+  decode_suite('m', gpr_time_from_millis);
+  decode_suite('S', gpr_time_from_seconds);
+  decode_suite('M', gpr_time_from_minutes);
+  decode_suite('H', gpr_time_from_hours);
+  assert_decodes_as("1000000000000000000000u", gpr_inf_future);
+}
+
+void test_decoding_fails() {
+  gpr_timespec x;
+  LOG_TEST();
+  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("", &x));
+  GPR_ASSERT(0 == grpc_chttp2_decode_timeout(" ", &x));
+  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("x", &x));
+  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("1", &x));
+  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("1x", &x));
+  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("1ux", &x));
+  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("!", &x));
+  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("n1", &x));
+  GPR_ASSERT(0 == grpc_chttp2_decode_timeout("-1u", &x));
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  test_encoding();
+  test_decoding();
+  test_decoding_fails();
+  return 0;
+}