/*
 * ngtcp2
 *
 * Copyright (c) 2017 ngtcp2 contributors
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "ngtcp2_pkt_test.h"

#include <stdio.h>

#include "ngtcp2_pkt.h"
#include "ngtcp2_test_helper.h"
#include "ngtcp2_conv.h"
#include "ngtcp2_cid.h"
#include "ngtcp2_str.h"
#include "ngtcp2_vec.h"

static const MunitTest tests[] = {
  munit_void_test(test_ngtcp2_pkt_decode_version_cid),
  munit_void_test(test_ngtcp2_pkt_decode_hd_long),
  munit_void_test(test_ngtcp2_pkt_decode_hd_short),
  munit_void_test(test_ngtcp2_pkt_decode_frame),
  munit_void_test(test_ngtcp2_pkt_decode_stream_frame),
  munit_void_test(test_ngtcp2_pkt_decode_ack_frame),
  munit_void_test(test_ngtcp2_pkt_decode_padding_frame),
  munit_void_test(test_ngtcp2_pkt_encode_stream_frame),
  munit_void_test(test_ngtcp2_pkt_encode_ack_frame),
  munit_void_test(test_ngtcp2_pkt_encode_ack_ecn_frame),
  munit_void_test(test_ngtcp2_pkt_encode_reset_stream_frame),
  munit_void_test(test_ngtcp2_pkt_encode_connection_close_frame),
  munit_void_test(test_ngtcp2_pkt_encode_connection_close_app_frame),
  munit_void_test(test_ngtcp2_pkt_encode_max_data_frame),
  munit_void_test(test_ngtcp2_pkt_encode_max_stream_data_frame),
  munit_void_test(test_ngtcp2_pkt_encode_max_streams_frame),
  munit_void_test(test_ngtcp2_pkt_encode_ping_frame),
  munit_void_test(test_ngtcp2_pkt_encode_data_blocked_frame),
  munit_void_test(test_ngtcp2_pkt_encode_stream_data_blocked_frame),
  munit_void_test(test_ngtcp2_pkt_encode_streams_blocked_frame),
  munit_void_test(test_ngtcp2_pkt_encode_new_connection_id_frame),
  munit_void_test(test_ngtcp2_pkt_encode_stop_sending_frame),
  munit_void_test(test_ngtcp2_pkt_encode_path_challenge_frame),
  munit_void_test(test_ngtcp2_pkt_encode_path_response_frame),
  munit_void_test(test_ngtcp2_pkt_encode_crypto_frame),
  munit_void_test(test_ngtcp2_pkt_encode_new_token_frame),
  munit_void_test(test_ngtcp2_pkt_encode_retire_connection_id_frame),
  munit_void_test(test_ngtcp2_pkt_encode_handshake_done_frame),
  munit_void_test(test_ngtcp2_pkt_encode_datagram_frame),
  munit_void_test(test_ngtcp2_pkt_adjust_pkt_num),
  munit_void_test(test_ngtcp2_pkt_validate_ack),
  munit_void_test(test_ngtcp2_pkt_write_stateless_reset),
  munit_void_test(test_ngtcp2_pkt_write_stateless_reset2),
  munit_void_test(test_ngtcp2_pkt_write_retry),
  munit_void_test(test_ngtcp2_pkt_write_version_negotiation),
  munit_void_test(test_ngtcp2_pkt_stream_max_datalen),
  munit_void_test(test_ngtcp2_pkt_split_vec_rand),
  munit_void_test(test_ngtcp2_pkt_split_vec_at),
  munit_void_test(test_ngtcp2_pkt_find_server_name),
  munit_void_test(test_ngtcp2_pkt_append_ping_and_padding),
  munit_void_test(test_ngtcp2_pkt_permutate_vec),
  munit_void_test(test_ngtcp2_pkt_remove_vec_partial),
  munit_void_test(test_ngtcp2_stateless_reset_token_eq),
  munit_test_end(),
};

const MunitSuite pkt_suite = {
  .prefix = "/pkt",
  .tests = tests,
};

static uint8_t null_data[4096];

static int null_retry_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
                              const ngtcp2_crypto_aead_ctx *aead_ctx,
                              const uint8_t *plaintext, size_t plaintextlen,
                              const uint8_t *nonce, size_t noncelen,
                              const uint8_t *aad, size_t aadlen) {
  (void)dest;
  (void)aead;
  (void)aead_ctx;
  (void)plaintext;
  (void)plaintextlen;
  (void)nonce;
  (void)noncelen;
  (void)aad;
  (void)aadlen;

  if (plaintextlen && plaintext != dest) {
    memcpy(dest, plaintext, plaintextlen);
  }
  memset(dest + plaintextlen, 0, NGTCP2_RETRY_TAGLEN);

  return 0;
}

void test_ngtcp2_pkt_decode_version_cid(void) {
  uint8_t buf[NGTCP2_MAX_UDP_PAYLOAD_SIZE];
  ngtcp2_version_cid vc;
  int rv;
  uint8_t *p;
  size_t i;

  /* Supported QUIC version */
  p = buf;
  *p++ = NGTCP2_HEADER_FORM_BIT;
  p = ngtcp2_put_uint32be(p, NGTCP2_PROTO_VER_V1);
  *p++ = NGTCP2_MAX_CIDLEN;
  p = ngtcp2_setmem(p, 0xF1, NGTCP2_MAX_CIDLEN);
  *p++ = NGTCP2_MAX_CIDLEN - 1;
  p = ngtcp2_setmem(p, 0xF2, NGTCP2_MAX_CIDLEN - 1);

  rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);

  assert_int(0, ==, rv);
  assert_uint32(NGTCP2_PROTO_VER_V1, ==, vc.version);
  assert_size(NGTCP2_MAX_CIDLEN, ==, vc.dcidlen);
  assert_ptr_equal(&buf[6], vc.dcid);
  assert_size(NGTCP2_MAX_CIDLEN - 1, ==, vc.scidlen);
  assert_ptr_equal(&buf[6 + NGTCP2_MAX_CIDLEN + 1], vc.scid);

  /* Fail if header is truncated. */
  for (i = 1; i < (size_t)(p - buf); ++i) {
    rv = ngtcp2_pkt_decode_version_cid(&vc, buf, i, 0);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }

  /* Unsupported QUIC version */
  memset(buf, 0, sizeof(buf));
  p = buf;
  *p++ = NGTCP2_HEADER_FORM_BIT;
  p = ngtcp2_put_uint32be(p, 0xFFFFFF00);
  *p++ = NGTCP2_MAX_CIDLEN;
  p = ngtcp2_setmem(p, 0xF1, NGTCP2_MAX_CIDLEN);
  *p++ = NGTCP2_MAX_CIDLEN - 1;
  p = ngtcp2_setmem(p, 0xF2, NGTCP2_MAX_CIDLEN - 1);

  rv = ngtcp2_pkt_decode_version_cid(&vc, buf, sizeof(buf), 0);

  assert_ptrdiff(NGTCP2_ERR_VERSION_NEGOTIATION, ==, rv);
  assert_uint32(0xFFFFFF00, ==, vc.version);
  assert_size(NGTCP2_MAX_CIDLEN, ==, vc.dcidlen);
  assert_ptr_equal(&buf[6], vc.dcid);
  assert_size(NGTCP2_MAX_CIDLEN - 1, ==, vc.scidlen);
  assert_ptr_equal(&buf[6 + NGTCP2_MAX_CIDLEN + 1], vc.scid);

  /* Fail if header is truncated. */
  for (i = 1; i < (size_t)(p - buf); ++i) {
    rv = ngtcp2_pkt_decode_version_cid(&vc, buf, i, 0);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }

  /* Unsupported QUIC version with UDP payload size < 1200 */
  p = buf;
  *p++ = NGTCP2_HEADER_FORM_BIT;
  p = ngtcp2_put_uint32be(p, 0xFFFFFF00);
  *p++ = NGTCP2_MAX_CIDLEN;
  p = ngtcp2_setmem(p, 0xF1, NGTCP2_MAX_CIDLEN);
  *p++ = NGTCP2_MAX_CIDLEN - 1;
  p = ngtcp2_setmem(p, 0xF2, NGTCP2_MAX_CIDLEN - 1);

  rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);

  assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);

  /* Supported QUIC version with long CID */
  p = buf;
  *p++ = NGTCP2_HEADER_FORM_BIT;
  p = ngtcp2_put_uint32be(p, NGTCP2_PROTO_VER_V1);
  *p++ = NGTCP2_MAX_CIDLEN + 1;
  p = ngtcp2_setmem(p, 0xF1, NGTCP2_MAX_CIDLEN + 1);
  *p++ = NGTCP2_MAX_CIDLEN;
  p = ngtcp2_setmem(p, 0xF2, NGTCP2_MAX_CIDLEN);

  rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);

  assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);

  /* Unsupported QUIC version with long CID */
  memset(buf, 0, sizeof(buf));
  p = buf;
  *p++ = NGTCP2_HEADER_FORM_BIT;
  p = ngtcp2_put_uint32be(p, 0xFFFFFF00);
  *p++ = NGTCP2_MAX_CIDLEN + 1;
  p = ngtcp2_setmem(p, 0xF1, NGTCP2_MAX_CIDLEN + 1);
  *p++ = NGTCP2_MAX_CIDLEN;
  ngtcp2_setmem(p, 0xF2, NGTCP2_MAX_CIDLEN);

  rv = ngtcp2_pkt_decode_version_cid(&vc, buf, sizeof(buf), 0);

  assert_ptrdiff(NGTCP2_ERR_VERSION_NEGOTIATION, ==, rv);
  assert_uint32(0xFFFFFF00, ==, vc.version);
  assert_size(NGTCP2_MAX_CIDLEN + 1, ==, vc.dcidlen);
  assert_ptr_equal(&buf[6], vc.dcid);
  assert_size(NGTCP2_MAX_CIDLEN, ==, vc.scidlen);
  assert_ptr_equal(&buf[6 + NGTCP2_MAX_CIDLEN + 1 + 1], vc.scid);

  /* VN */
  p = buf;
  *p++ = NGTCP2_HEADER_FORM_BIT;
  p = ngtcp2_put_uint32be(p, 0);
  *p++ = NGTCP2_MAX_CIDLEN;
  p = ngtcp2_setmem(p, 0xF1, NGTCP2_MAX_CIDLEN);
  *p++ = NGTCP2_MAX_CIDLEN - 1;
  p = ngtcp2_setmem(p, 0xF2, NGTCP2_MAX_CIDLEN - 1);

  rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);

  assert_int(0, ==, rv);
  assert_uint32(0, ==, vc.version);
  assert_size(NGTCP2_MAX_CIDLEN, ==, vc.dcidlen);
  assert_ptr_equal(&buf[6], vc.dcid);
  assert_size(NGTCP2_MAX_CIDLEN - 1, ==, vc.scidlen);
  assert_ptr_equal(&buf[6 + NGTCP2_MAX_CIDLEN + 1], vc.scid);

  /* Fail if header is truncated. */
  for (i = 1; i < (size_t)(p - buf); ++i) {
    rv = ngtcp2_pkt_decode_version_cid(&vc, buf, i, 0);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }

  /* VN with long CID */
  p = buf;
  *p++ = NGTCP2_HEADER_FORM_BIT;
  p = ngtcp2_put_uint32be(p, 0);
  *p++ = NGTCP2_MAX_CIDLEN + 1;
  p = ngtcp2_setmem(p, 0xF1, NGTCP2_MAX_CIDLEN + 1);
  *p++ = NGTCP2_MAX_CIDLEN;
  p = ngtcp2_setmem(p, 0xF2, NGTCP2_MAX_CIDLEN);

  rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);

  assert_int(0, ==, rv);
  assert_uint32(0, ==, vc.version);
  assert_size(NGTCP2_MAX_CIDLEN + 1, ==, vc.dcidlen);
  assert_ptr_equal(&buf[6], vc.dcid);
  assert_size(NGTCP2_MAX_CIDLEN, ==, vc.scidlen);
  assert_ptr_equal(&buf[6 + NGTCP2_MAX_CIDLEN + 1 + 1], vc.scid);

  /* Malformed Long packet */
  p = buf;
  *p++ = NGTCP2_HEADER_FORM_BIT;
  p = ngtcp2_put_uint32be(p, NGTCP2_PROTO_VER_V1);
  *p++ = NGTCP2_MAX_CIDLEN;
  p = ngtcp2_setmem(p, 0xF1, NGTCP2_MAX_CIDLEN);
  *p++ = NGTCP2_MAX_CIDLEN - 1;
  p = ngtcp2_setmem(p, 0xF2, NGTCP2_MAX_CIDLEN - 1);
  --p;

  rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0);

  assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);

  /* Short packet */
  p = buf;
  *p++ = 0;
  p = ngtcp2_setmem(p, 0xF1, NGTCP2_MAX_CIDLEN);

  rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf),
                                     NGTCP2_MAX_CIDLEN);

  assert_int(0, ==, rv);
  assert_uint32(0, ==, vc.version);
  assert_ptr_equal(&buf[1], vc.dcid);
  assert_size(NGTCP2_MAX_CIDLEN, ==, vc.dcidlen);
  assert_ptr_equal(NULL, vc.scid);
  assert_size(0, ==, vc.scidlen);

  /* Fail if header is truncated. */
  for (i = 1; i < (size_t)(p - buf); ++i) {
    rv = ngtcp2_pkt_decode_version_cid(&vc, buf, i, NGTCP2_MAX_CIDLEN);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }
}

void test_ngtcp2_pkt_decode_hd_long(void) {
  ngtcp2_pkt_hd hd, nhd;
  uint8_t buf[256];
  ngtcp2_ssize rv;
  static const ngtcp2_cid dcid = make_dcid();
  static const ngtcp2_cid scid = make_scid();
  size_t len;
  size_t i;

  /* Handshake */
  ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_HANDSHAKE,
                     &dcid, &scid, 0xE1E2E3E4U, 4, NGTCP2_PROTO_VER_V1);
  hd.len = 16383;

  rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd);

  len = 1 + 4 + 1 + dcid.datalen + 1 + scid.datalen + NGTCP2_PKT_LENGTHLEN + 4;

  assert_ptrdiff((ngtcp2_ssize)len, ==, rv);
  assert_true(buf[0] & NGTCP2_FIXED_BIT_MASK);

  rv = pkt_decode_hd_long(&nhd, buf, len);

  assert_ptrdiff((ngtcp2_ssize)len, ==, rv);
  assert_uint8(hd.type, ==, nhd.type);
  assert_uint8(hd.flags, ==, nhd.flags);
  assert_true(ngtcp2_cid_eq(&hd.dcid, &nhd.dcid));
  assert_true(ngtcp2_cid_eq(&hd.scid, &nhd.scid));
  assert_int64(0xE1E2E3E4U, ==, nhd.pkt_num);
  assert_uint32(hd.version, ==, nhd.version);
  assert_size(hd.len, ==, nhd.len);

  /* Fail if header is truncated. */
  for (i = 0; i < len; ++i) {
    rv = pkt_decode_hd_long(&nhd, buf, i);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }

  /* Handshake without Fixed Bit set */
  ngtcp2_pkt_hd_init(
    &hd, NGTCP2_PKT_FLAG_LONG_FORM | NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR,
    NGTCP2_PKT_HANDSHAKE, &dcid, &scid, 0xE1E2E3E4U, 4, NGTCP2_PROTO_VER_V1);
  hd.len = 16383;

  rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd);

  len = 1 + 4 + 1 + dcid.datalen + 1 + scid.datalen + NGTCP2_PKT_LENGTHLEN + 4;

  assert_ptrdiff((ngtcp2_ssize)len, ==, rv);
  assert_false((buf[0] & NGTCP2_FIXED_BIT_MASK));

  rv = pkt_decode_hd_long(&nhd, buf, len);

  assert_ptrdiff((ngtcp2_ssize)len, ==, rv);
  assert_uint8(hd.type, ==, nhd.type);
  assert_uint8(hd.flags, ==, nhd.flags);
  assert_true(ngtcp2_cid_eq(&hd.dcid, &nhd.dcid));
  assert_true(ngtcp2_cid_eq(&hd.scid, &nhd.scid));
  assert_int64(0xE1E2E3E4U, ==, nhd.pkt_num);
  assert_uint32(hd.version, ==, nhd.version);
  assert_size(hd.len, ==, nhd.len);

  /* Fail if header is truncated. */
  for (i = 0; i < len; ++i) {
    rv = pkt_decode_hd_long(&nhd, buf, i);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }

  /* VN */
  /* Set random packet type */
  ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_HANDSHAKE,
                     &dcid, &scid, 0, 4, NGTCP2_PROTO_VER_V1);

  rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd);
  /* Set version field to 0 */
  memset(&buf[1], 0, 4);

  len = 1 + 4 + 1 + dcid.datalen + 1 + scid.datalen;

  assert_ptrdiff((ngtcp2_ssize)len, ==,
                 rv - NGTCP2_PKT_LENGTHLEN - 4 /* pkt_num */);

  rv = pkt_decode_hd_long(&nhd, buf, len);

  assert_ptrdiff((ngtcp2_ssize)len, ==, rv);
  assert_uint8(NGTCP2_PKT_VERSION_NEGOTIATION, ==, nhd.type);
  assert_uint8((uint8_t)(hd.flags & ~NGTCP2_PKT_FLAG_LONG_FORM), ==, nhd.flags);
  assert_true(ngtcp2_cid_eq(&hd.dcid, &nhd.dcid));
  assert_true(ngtcp2_cid_eq(&hd.scid, &nhd.scid));
  assert_int64(hd.pkt_num, ==, nhd.pkt_num);
  assert_uint32(0, ==, nhd.version);
  assert_size(hd.len, ==, nhd.len);

  /* Fail if header is truncated. */
  for (i = 0; i < len; ++i) {
    rv = pkt_decode_hd_long(&nhd, buf, i);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }
}

void test_ngtcp2_pkt_decode_hd_short(void) {
  ngtcp2_pkt_hd hd, nhd;
  uint8_t buf[256];
  ngtcp2_ssize rv;
  size_t expectedlen;
  static const ngtcp2_cid dcid = make_dcid();
  size_t i;

  /* 4 bytes packet number */
  ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL,
                     0xE1E2E3E4U, 4, 0xD1D2D3D4U);

  expectedlen = 1 + dcid.datalen + 4;

  rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_true(buf[0] & NGTCP2_FIXED_BIT_MASK);

  rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_uint8(hd.flags, ==, nhd.flags);
  assert_uint8(NGTCP2_PKT_1RTT, ==, nhd.type);
  assert_true(ngtcp2_cid_eq(&dcid, &nhd.dcid));
  assert_true(ngtcp2_cid_empty(&nhd.scid));
  assert_int64(0xE1E2E3E4U, ==, nhd.pkt_num);
  assert_size(hd.pkt_numlen, ==, nhd.pkt_numlen);
  assert_uint32(0, ==, nhd.version);
  assert_size(0, ==, nhd.len);

  /* Fail if header is truncated. */
  for (i = 0; i < expectedlen; ++i) {
    rv = pkt_decode_hd_short(&nhd, buf, i, dcid.datalen);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }

  /* 4 bytes packet number without Fixed Bit set */
  ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR, NGTCP2_PKT_1RTT,
                     &dcid, NULL, 0xE1E2E3E4U, 4, 0xD1D2D3D4U);

  expectedlen = 1 + dcid.datalen + 4;

  rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_false((buf[0] & NGTCP2_FIXED_BIT_MASK));

  rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_uint8(hd.flags, ==, nhd.flags);
  assert_uint8(NGTCP2_PKT_1RTT, ==, nhd.type);
  assert_true(ngtcp2_cid_eq(&dcid, &nhd.dcid));
  assert_true(ngtcp2_cid_empty(&nhd.scid));
  assert_int64(0xE1E2E3E4U, ==, nhd.pkt_num);
  assert_size(hd.pkt_numlen, ==, nhd.pkt_numlen);
  assert_uint32(0, ==, nhd.version);
  assert_size(0, ==, nhd.len);

  /* Fail if header is truncated. */
  for (i = 0; i < expectedlen; ++i) {
    rv = pkt_decode_hd_short(&nhd, buf, i, dcid.datalen);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }

  /* 2 bytes packet number */
  ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL,
                     0xE1E2E3E4U, 2, 0xD1D2D3D4U);

  expectedlen = 1 + dcid.datalen + 2;

  rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);

  rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_uint8(hd.flags, ==, nhd.flags);
  assert_uint8(NGTCP2_PKT_1RTT, ==, nhd.type);
  assert_true(ngtcp2_cid_eq(&dcid, &nhd.dcid));
  assert_true(ngtcp2_cid_empty(&nhd.scid));
  assert_int64(0xE3E4U, ==, nhd.pkt_num);
  assert_size(hd.pkt_numlen, ==, nhd.pkt_numlen);
  assert_uint32(0, ==, nhd.version);
  assert_size(0, ==, nhd.len);

  /* Fail if header is truncated. */
  for (i = 0; i < expectedlen; ++i) {
    rv = pkt_decode_hd_short(&nhd, buf, i, dcid.datalen);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }

  /* 1 byte packet number */
  ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL,
                     0xE1E2E3E4U, 1, 0xD1D2D3D4U);

  expectedlen = 1 + dcid.datalen + 1;

  rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);

  rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_uint8(hd.flags, ==, nhd.flags);
  assert_uint8(NGTCP2_PKT_1RTT, ==, nhd.type);
  assert_true(ngtcp2_cid_eq(&dcid, &nhd.dcid));
  assert_true(ngtcp2_cid_empty(&nhd.scid));
  assert_int64(0xE4, ==, nhd.pkt_num);
  assert_size(hd.pkt_numlen, ==, nhd.pkt_numlen);
  assert_uint32(0, ==, nhd.version);
  assert_size(0, ==, nhd.len);

  /* Fail if header is truncated. */
  for (i = 0; i < expectedlen; ++i) {
    rv = pkt_decode_hd_short(&nhd, buf, i, dcid.datalen);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }

  /* With Key Phase */
  ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_KEY_PHASE, NGTCP2_PKT_1RTT, &dcid,
                     NULL, 0xE1E2E3E4U, 4, 0xD1D2D3D4U);

  expectedlen = 1 + dcid.datalen + 4;

  rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);

  rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  /* key phase bit is protected by header protection and
     ngtcp2_pkt_decode_hd_short does not decode it. */
  assert_uint8(NGTCP2_PKT_FLAG_NONE, ==, nhd.flags);
  assert_uint8(NGTCP2_PKT_1RTT, ==, nhd.type);
  assert_true(ngtcp2_cid_eq(&dcid, &nhd.dcid));
  assert_true(ngtcp2_cid_empty(&nhd.scid));
  assert_int64(0xE1E2E3E4U, ==, nhd.pkt_num);
  assert_size(hd.pkt_numlen, ==, nhd.pkt_numlen);
  assert_uint32(0, ==, nhd.version);
  assert_size(0, ==, nhd.len);

  /* Fail if header is truncated. */
  for (i = 0; i < expectedlen; ++i) {
    rv = pkt_decode_hd_short(&nhd, buf, i, dcid.datalen);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }

  /* With empty DCID */
  ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, NULL, NULL,
                     0xE1E2E3E4U, 4, 0xD1D2D3D4U);

  expectedlen = 1 + 4;

  rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);

  rv = pkt_decode_hd_short(&nhd, buf, expectedlen, 0);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_uint8(hd.flags, ==, nhd.flags);
  assert_uint8(NGTCP2_PKT_1RTT, ==, nhd.type);
  assert_true(ngtcp2_cid_empty(&nhd.dcid));
  assert_true(ngtcp2_cid_empty(&nhd.scid));
  assert_int64(0xE1E2E3E4U, ==, nhd.pkt_num);
  assert_size(hd.pkt_numlen, ==, nhd.pkt_numlen);
  assert_uint32(0, ==, nhd.version);
  assert_size(0, ==, nhd.len);

  /* Fail if header is truncated. */
  for (i = 0; i < expectedlen; ++i) {
    rv = pkt_decode_hd_short(&nhd, buf, i, dcid.datalen);

    assert_ptrdiff(NGTCP2_ERR_INVALID_ARGUMENT, ==, rv);
  }
}

void test_ngtcp2_pkt_decode_frame(void) {
  static const uint8_t malformed_stream_frame[] = {
    0xFF, 0x01, 0x01, 0x01, 0x01,
  };
  static const uint8_t good_stream_frame[] = {
    0x0F, 0x01, 0x01, 0x01, 0x01,
  };
  ngtcp2_ssize rv;
  ngtcp2_frame_decoder frd;
  ngtcp2_frame fr;

  rv = ngtcp2_frame_decoder_decode(&frd, &fr, malformed_stream_frame,
                                   sizeof(malformed_stream_frame));

  assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);

  rv = ngtcp2_frame_decoder_decode(&frd, &fr, good_stream_frame,
                                   sizeof(good_stream_frame));

  assert_ptrdiff(5, ==, rv);
  assert_uint64(NGTCP2_FRAME_STREAM, ==, fr.hd.type);
  assert_uint8(0x7, ==, fr.stream.flags);
  assert_int64(1, ==, fr.stream.stream_id);
  assert_uint64(1, ==, fr.stream.offset);
  assert_true(fr.stream.fin);
  assert_size(1, ==, fr.stream.datacnt);
  assert_size(1, ==, fr.stream.data[0].len);
}

void test_ngtcp2_pkt_decode_stream_frame(void) {
  uint8_t buf[256];
  size_t buflen;
  ngtcp2_vec datav;
  ngtcp2_stream fr;
  ngtcp2_ssize rv;
  size_t expectedlen;
  size_t i;

  /* 32 bits Stream ID + 62 bits Offset + Data Length */
  buflen = ngtcp2_t_encode_stream_frame(buf, NGTCP2_STREAM_LEN_BIT, 0xF1F2F3F4U,
                                        0x31F2F3F4F5F6F7F8ULL, 0x14);

  expectedlen = 1 + 8 + 8 + 1 + 20;

  assert_size(expectedlen, ==, buflen);

  fr.data = &datav;
  rv = ngtcp2_pkt_decode_stream_frame(&fr, buf, buflen);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_false(fr.fin);
  assert_int64(0xF1F2F3F4U, ==, fr.stream_id);
  assert_uint64(0x31F2F3F4F5F6F7F8ULL, ==, fr.offset);
  assert_size(1, ==, fr.datacnt);
  assert_size(0x14, ==, fr.data[0].len);

  /* Fail if a frame is truncated */
  for (i = 1; i < buflen; ++i) {
    fr.data = &datav;
    rv = ngtcp2_pkt_decode_stream_frame(&fr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  fr = (ngtcp2_stream){0};

  /* 6 bits Stream ID + no Offset + Data Length */
  buflen =
    ngtcp2_t_encode_stream_frame(buf, NGTCP2_STREAM_LEN_BIT, 0x31, 0x00, 0x14);

  expectedlen = 1 + 1 + 0 + 1 + 20;

  assert_size(expectedlen, ==, buflen);

  fr.data = &datav;
  rv = ngtcp2_pkt_decode_stream_frame(&fr, buf, buflen);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_false(fr.fin);
  assert_int64(0x31, ==, fr.stream_id);
  assert_uint64(0x00, ==, fr.offset);
  assert_size(1, ==, fr.datacnt);
  assert_size(0x14, ==, fr.data[0].len);

  /* Cutting 1 bytes from the tail must cause invalid argument
     error */
  fr.data = &datav;
  rv = ngtcp2_pkt_decode_stream_frame(&fr, buf, buflen - 1);

  assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);

  fr = (ngtcp2_stream){0};

  /* Fin bit set + no Data Length */
  buflen = ngtcp2_t_encode_stream_frame(buf, NGTCP2_STREAM_FIN_BIT, 0x31F2F3F4U,
                                        0x00, 0x14);

  expectedlen = 1 + 4 + 20;

  assert_size(expectedlen, ==, buflen);

  fr.data = &datav;
  rv = ngtcp2_pkt_decode_stream_frame(&fr, buf, buflen);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_true(fr.fin);
  assert_int64(0x31F2F3F4U, ==, fr.stream_id);
  assert_uint64(0x00, ==, fr.offset);
  assert_size(1, ==, fr.datacnt);
  assert_size(0x14, ==, fr.data[0].len);

  fr = (ngtcp2_stream){0};
}

void test_ngtcp2_pkt_decode_ack_frame(void) {
  uint8_t buf[256];
  size_t buflen;
  ngtcp2_ack_range ack_ranges[NGTCP2_MAX_ACK_RANGES];
  ngtcp2_ack fr;
  ngtcp2_ssize rv;
  size_t expectedlen;

  /* 62 bits Largest Acknowledged */
  buflen =
    ngtcp2_t_encode_ack_frame(buf, 0x31F2F3F4F5F6F7F8ULL, 0x31E2E3E4E5E6E7E8ULL,
                              99, 0x31D2D3D4D5D6D7D8ULL);

  expectedlen = 1 + 8 + 1 + 1 + 8 + 2 + 8;

  assert_size(expectedlen, ==, buflen);

  fr.ranges = ack_ranges;
  rv = ngtcp2_pkt_decode_ack_frame(&fr, buf, buflen);

  assert_ptrdiff((ngtcp2_ssize)expectedlen, ==, rv);
  assert_int64(0x31F2F3F4F5F6F7F8ULL, ==, fr.largest_ack);
  assert_size(1, ==, fr.rangecnt);
  assert_uint64(0x31E2E3E4E5E6E7E8ULL, ==, fr.first_ack_range);
  assert_uint64(99, ==, fr.ranges[0].gap);
  assert_uint64(0x31D2D3D4D5D6D7D8ULL, ==, fr.ranges[0].len);
}

void test_ngtcp2_pkt_decode_padding_frame(void) {
  uint8_t buf[256];
  ngtcp2_padding fr;
  ngtcp2_ssize rv;
  size_t paddinglen = 31;

  memset(buf, 0, paddinglen);
  buf[paddinglen] = NGTCP2_FRAME_STREAM;

  rv = ngtcp2_pkt_decode_padding_frame(&fr, buf, paddinglen + 1);

  assert_ptrdiff((ngtcp2_ssize)paddinglen, ==, rv);
  assert_size((size_t)31, ==, fr.len);
}

void test_ngtcp2_pkt_encode_stream_frame(void) {
  static const uint8_t data[] = "0123456789abcdef0";
  uint8_t buf[256];
  ngtcp2_vec datav, ndatav;
  ngtcp2_stream fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen;
  size_t i;

  /* 32 bits Stream ID + 62 bits Offset + Data Length */
  fr = (ngtcp2_stream){
    .type = NGTCP2_FRAME_STREAM,
    .stream_id = 0xF1F2F3F4U,
    .offset = 0x31F2F3F4F5F6F7F8ULL,
    .datacnt = 1,
    .data = &datav,
  };
  datav = (ngtcp2_vec){
    .len = ngtcp2_strlen_lit(data),
    .base = (uint8_t *)data,
  };

  framelen = 1 + 8 + 8 + 1 + 17;

  rv = ngtcp2_pkt_encode_stream_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.data = &ndatav;
  rv = ngtcp2_pkt_decode_stream_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint8((NGTCP2_STREAM_OFF_BIT | NGTCP2_STREAM_LEN_BIT), ==, nfr.flags);
  assert_int(fr.fin, ==, nfr.fin);
  assert_int64(fr.stream_id, ==, nfr.stream_id);
  assert_uint64(fr.offset, ==, nfr.offset);
  assert_size(1, ==, nfr.datacnt);
  assert_size(fr.data[0].len, ==, nfr.data[0].len);
  assert_memory_equal(fr.data[0].len, fr.data[0].base, nfr.data[0].base);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.data = &ndatav;
    rv = ngtcp2_pkt_decode_stream_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_stream){0};

  /* 6 bits Stream ID + No Offset + Data Length */
  fr = (ngtcp2_stream){
    .type = NGTCP2_FRAME_STREAM,
    .stream_id = 0x31,
    .datacnt = 1,
    .data = &datav,
  };
  datav = (ngtcp2_vec){
    .len = ngtcp2_strlen_lit(data),
    .base = (uint8_t *)data,
  };

  framelen = 1 + 1 + 1 + 17;

  rv = ngtcp2_pkt_encode_stream_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.data = &ndatav;
  rv = ngtcp2_pkt_decode_stream_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint8(NGTCP2_STREAM_LEN_BIT, ==, nfr.flags);
  assert_int(fr.fin, ==, nfr.fin);
  assert_int64(fr.stream_id, ==, nfr.stream_id);
  assert_uint64(fr.offset, ==, nfr.offset);
  assert_size(1, ==, nfr.datacnt);
  assert_size(fr.data[0].len, ==, nfr.data[0].len);
  assert_memory_equal(fr.data[0].len, fr.data[0].base, nfr.data[0].base);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.data = &ndatav;
    rv = ngtcp2_pkt_decode_stream_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_stream){0};

  /* Fin + 32 bits Stream ID + 62 bits Offset + Data Length */
  fr = (ngtcp2_stream){
    .type = NGTCP2_FRAME_STREAM,
    .fin = 1,
    .stream_id = 0xF1F2F3F4U,
    .offset = 0x31F2F3F4F5F6F7F8ULL,
    .datacnt = 1,
    .data = &datav,
  };
  datav = (ngtcp2_vec){
    .len = ngtcp2_strlen_lit(data),
    .base = (uint8_t *)data,
  };

  framelen = 1 + 8 + 8 + 1 + 17;

  rv = ngtcp2_pkt_encode_stream_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.data = &ndatav;
  rv = ngtcp2_pkt_decode_stream_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint8(
    (NGTCP2_STREAM_FIN_BIT | NGTCP2_STREAM_OFF_BIT | NGTCP2_STREAM_LEN_BIT), ==,
    nfr.flags);
  assert_int(fr.fin, ==, nfr.fin);
  assert_int64(fr.stream_id, ==, nfr.stream_id);
  assert_uint64(fr.offset, ==, nfr.offset);
  assert_size(1, ==, nfr.datacnt);
  assert_size(fr.data[0].len, ==, nfr.data[0].len);
  assert_memory_equal(fr.data[0].len, fr.data[0].base, nfr.data[0].base);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.data = &ndatav;
    rv = ngtcp2_pkt_decode_stream_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_stream){0};

  /* NOBUF: Fin + 32 bits Stream ID + 62 bits Offset + Data Length */
  fr = (ngtcp2_stream){
    .type = NGTCP2_FRAME_STREAM,
    .fin = 1,
    .stream_id = 0xF1F2F3F4U,
    .offset = 0x31F2F3F4F5F6F7F8ULL,
    .datacnt = 1,
    .data = &datav,
  };
  datav = (ngtcp2_vec){
    .len = ngtcp2_strlen_lit(data),
    .base = (uint8_t *)data,
  };

  framelen = 1 + 8 + 8 + 1 + 17;

  rv = ngtcp2_pkt_encode_stream_frame(buf, framelen - 1, &fr);

  assert_ptrdiff(NGTCP2_ERR_NOBUF, ==, rv);
}

void test_ngtcp2_pkt_encode_ack_frame(void) {
  uint8_t buf[256];
  ngtcp2_ack_range ack_ranges[NGTCP2_MAX_ACK_RANGES],
    nack_ranges[NGTCP2_MAX_ACK_RANGES];
  ngtcp2_ack fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen;
  size_t i;

  /* 0 Num Blocks */
  fr = (ngtcp2_ack){
    .type = NGTCP2_FRAME_ACK,
    .largest_ack = 0xF1F2F3F4ULL,
  };

  framelen = 1 + 8 + 1 + 1 + 1;

  rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.ranges = nack_ranges;
  rv = ngtcp2_pkt_decode_ack_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_int64(fr.largest_ack, ==, nfr.largest_ack);
  assert_uint64(fr.ack_delay, ==, nfr.ack_delay);
  assert_size(fr.rangecnt, ==, nfr.rangecnt);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.ranges = nack_ranges;
    rv = ngtcp2_pkt_decode_ack_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_ack){0};
  memset(nack_ranges, 0, sizeof(nack_ranges));

  /* 2 Num Blocks */
  fr = (ngtcp2_ack){
    .type = NGTCP2_FRAME_ACK,
    .largest_ack = 0xF1F2F3F4ULL,
    .first_ack_range = 0xE1E2E3E4ULL,
    .ack_delay = 0xF1F2,
    .rangecnt = 2,
    .ranges = ack_ranges,
  };
  ack_ranges[0] = (ngtcp2_ack_range){
    .gap = 255,
    .len = 0xD1D2D3D4ULL,
  };
  ack_ranges[1] = (ngtcp2_ack_range){
    .gap = 1,
    .len = 0xD1D2D3D4ULL,
  };

  framelen = 1 + 8 + 4 + 1 + 8 + (2 + 8) + (1 + 8);

  rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.ranges = nack_ranges;
  rv = ngtcp2_pkt_decode_ack_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_int64(fr.largest_ack, ==, nfr.largest_ack);
  assert_uint64(fr.ack_delay, ==, nfr.ack_delay);
  assert_size(fr.rangecnt, ==, nfr.rangecnt);

  for (i = 0; i < fr.rangecnt; ++i) {
    assert_uint64(fr.ranges[i].gap, ==, nfr.ranges[i].gap);
    assert_uint64(fr.ranges[i].len, ==, nfr.ranges[i].len);
  }

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.ranges = nack_ranges;
    rv = ngtcp2_pkt_decode_ack_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_ack){0};
  memset(nack_ranges, 0, sizeof(nack_ranges));
}

void test_ngtcp2_pkt_encode_ack_ecn_frame(void) {
  uint8_t buf[256];
  ngtcp2_ack_range ack_ranges[NGTCP2_MAX_ACK_RANGES],
    nack_ranges[NGTCP2_MAX_ACK_RANGES];
  ngtcp2_ack fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen;
  size_t i;

  /* 0 Num Blocks */
  fr = (ngtcp2_ack){
    .type = NGTCP2_FRAME_ACK_ECN,
    .largest_ack = 0xF1F2F3F4ULL,
    .ecn =
      {
        .ect0 = 64,
        .ect1 = 16384,
        .ce = 1073741824,
      },
  };

  framelen = 1 + 8 + 1 + 1 + 1 + 2 + 4 + 8;

  rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.ranges = nack_ranges;
  rv = ngtcp2_pkt_decode_ack_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_int64(fr.largest_ack, ==, nfr.largest_ack);
  assert_uint64(fr.ack_delay, ==, nfr.ack_delay);
  assert_size(fr.rangecnt, ==, nfr.rangecnt);
  assert_uint64(fr.ecn.ect0, ==, nfr.ecn.ect0);
  assert_uint64(fr.ecn.ect1, ==, nfr.ecn.ect1);
  assert_uint64(fr.ecn.ce, ==, nfr.ecn.ce);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.ranges = nack_ranges;
    rv = ngtcp2_pkt_decode_ack_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_ack){0};
  memset(nack_ranges, 0, sizeof(nack_ranges));

  /* 2 Num Blocks */
  fr = (ngtcp2_ack){
    .type = NGTCP2_FRAME_ACK_ECN,
    .largest_ack = 0xF1F2F3F4ULL,
    .first_ack_range = 0xE1E2E3E4ULL,
    .ack_delay = 0xF1F2,
    .rangecnt = 2,
    .ecn =
      {
        .ect1 = 64,
        .ce = 16384,
      },
    .ranges = ack_ranges,
  };
  ack_ranges[0] = (ngtcp2_ack_range){
    .gap = 255,
    .len = 0xD1D2D3D4ULL,
  };
  ack_ranges[1] = (ngtcp2_ack_range){
    .gap = 1,
    .len = 0xD1D2D3D4ULL,
  };

  framelen = 1 + 8 + 4 + 1 + 8 + (2 + 8) + (1 + 8) + 1 + 2 + 4;

  rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.ranges = nack_ranges;
  rv = ngtcp2_pkt_decode_ack_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_int64(fr.largest_ack, ==, nfr.largest_ack);
  assert_uint64(fr.ack_delay, ==, nfr.ack_delay);
  assert_size(fr.rangecnt, ==, nfr.rangecnt);

  for (i = 0; i < fr.rangecnt; ++i) {
    assert_uint64(fr.ranges[i].gap, ==, nfr.ranges[i].gap);
    assert_uint64(fr.ranges[i].len, ==, nfr.ranges[i].len);
  }

  assert_uint64(fr.ecn.ect0, ==, nfr.ecn.ect0);
  assert_uint64(fr.ecn.ect1, ==, nfr.ecn.ect1);
  assert_uint64(fr.ecn.ce, ==, nfr.ecn.ce);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.ranges = nack_ranges;
    rv = ngtcp2_pkt_decode_ack_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_ack){0};
  memset(nack_ranges, 0, sizeof(nack_ranges));
}

void test_ngtcp2_pkt_encode_reset_stream_frame(void) {
  uint8_t buf[32];
  ngtcp2_reset_stream fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 4 + 4 + 8;
  size_t i;

  fr = (ngtcp2_reset_stream){
    .type = NGTCP2_FRAME_RESET_STREAM,
    .stream_id = 1000000007,
    .app_error_code = 0xE1E2,
    .final_size = 0x31F2F3F4F5F6F7F8ULL,
  };

  rv = ngtcp2_pkt_encode_reset_stream_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_reset_stream_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_int64(fr.stream_id, ==, nfr.stream_id);
  assert_uint64(fr.app_error_code, ==, nfr.app_error_code);
  assert_uint64(fr.final_size, ==, nfr.final_size);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_reset_stream_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_connection_close_frame(void) {
  uint8_t buf[2048];
  ngtcp2_connection_close fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen;
  uint8_t reason[1024];
  size_t i;

  memset(reason, 0xFA, sizeof(reason));

  /* no Reason Phrase */
  fr = (ngtcp2_connection_close){
    .type = NGTCP2_FRAME_CONNECTION_CLOSE,
    .error_code = 0xF1F2U,
    .frame_type = 255,
  };

  framelen = 1 + 4 + 2 + 1;

  rv = ngtcp2_pkt_encode_connection_close_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_connection_close_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint64(fr.error_code, ==, nfr.error_code);
  assert_size(fr.reasonlen, ==, nfr.reasonlen);
  assert_ptr_equal(fr.reason, nfr.reason);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_connection_close_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_connection_close){0};

  /* 1024 bytes Reason Phrase */
  fr = (ngtcp2_connection_close){
    .type = NGTCP2_FRAME_CONNECTION_CLOSE,
    .error_code = 0xF3F4U,
    .reasonlen = sizeof(reason),
    .reason = reason,
  };

  framelen = 1 + 4 + 1 + 2 + sizeof(reason);

  rv = ngtcp2_pkt_encode_connection_close_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_connection_close_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint64(fr.error_code, ==, nfr.error_code);
  assert_size(fr.reasonlen, ==, nfr.reasonlen);
  assert_memory_equal(sizeof(reason), reason, nfr.reason);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_connection_close_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_connection_close){0};
}

void test_ngtcp2_pkt_encode_connection_close_app_frame(void) {
  uint8_t buf[2048];
  ngtcp2_connection_close fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen;
  uint8_t reason[1024];
  size_t i;

  memset(reason, 0xFA, sizeof(reason));

  /* no Reason Phrase */
  fr = (ngtcp2_connection_close){
    .type = NGTCP2_FRAME_CONNECTION_CLOSE_APP,
    .error_code = 0xF1F2U,
    .frame_type = 0xFF, /* This must be ignored. */
  };

  framelen = 1 + 4 + 1;

  rv = ngtcp2_pkt_encode_connection_close_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_connection_close_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint64(fr.error_code, ==, nfr.error_code);
  assert_uint64(0, ==, nfr.frame_type);
  assert_size(fr.reasonlen, ==, nfr.reasonlen);
  assert_ptr_equal(fr.reason, nfr.reason);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_connection_close_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_connection_close){0};
}

void test_ngtcp2_pkt_encode_max_data_frame(void) {
  uint8_t buf[16];
  ngtcp2_max_data fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 8;
  size_t i;

  fr = (ngtcp2_max_data){
    .type = NGTCP2_FRAME_MAX_DATA,
    .max_data = 0x31F2F3F4F5F6F7F8ULL,
  };

  rv = ngtcp2_pkt_encode_max_data_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_max_data_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint64(fr.max_data, ==, nfr.max_data);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_max_data_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_max_stream_data_frame(void) {
  uint8_t buf[17];
  ngtcp2_max_stream_data fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 8 + 8;
  size_t i;

  fr = (ngtcp2_max_stream_data){
    .type = NGTCP2_FRAME_MAX_STREAM_DATA,
    .stream_id = 0xF1F2F3F4U,
    .max_stream_data = 0x35F6F7F8F9FAFBFCULL,
  };

  rv = ngtcp2_pkt_encode_max_stream_data_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_max_stream_data_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_int64(fr.stream_id, ==, nfr.stream_id);
  assert_uint64(fr.max_stream_data, ==, nfr.max_stream_data);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_max_stream_data_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_max_streams_frame(void) {
  uint8_t buf[16];
  ngtcp2_max_streams fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 8;
  size_t i;

  fr = (ngtcp2_max_streams){
    .type = NGTCP2_FRAME_MAX_STREAMS_BIDI,
    .max_streams = 0xF1F2F3F4U,
  };

  rv = ngtcp2_pkt_encode_max_streams_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_max_streams_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint64(fr.max_streams, ==, nfr.max_streams);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_max_streams_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_ping_frame(void) {
  uint8_t buf[3];
  ngtcp2_ping fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen;

  fr.type = NGTCP2_FRAME_PING;

  framelen = 1;

  rv = ngtcp2_pkt_encode_ping_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_ping_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
}

void test_ngtcp2_pkt_encode_data_blocked_frame(void) {
  uint8_t buf[9];
  ngtcp2_data_blocked fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 8;
  size_t i;

  fr = (ngtcp2_data_blocked){
    .type = NGTCP2_FRAME_DATA_BLOCKED,
    .offset = 0x31F2F3F4F5F6F7F8ULL,
  };

  rv = ngtcp2_pkt_encode_data_blocked_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_data_blocked_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint64(fr.offset, ==, nfr.offset);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_data_blocked_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_stream_data_blocked_frame(void) {
  uint8_t buf[17];
  ngtcp2_stream_data_blocked fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 8 + 8;
  size_t i;

  fr = (ngtcp2_stream_data_blocked){
    .type = NGTCP2_FRAME_STREAM_DATA_BLOCKED,
    .stream_id = 0xF1F2F3F4U,
    .offset = 0x35F6F7F8F9FAFBFCULL,
  };

  rv = ngtcp2_pkt_encode_stream_data_blocked_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_stream_data_blocked_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_int64(fr.stream_id, ==, nfr.stream_id);
  assert_uint64(fr.offset, ==, nfr.offset);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_stream_data_blocked_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_streams_blocked_frame(void) {
  uint8_t buf[9];
  ngtcp2_streams_blocked fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 8;
  size_t i;

  fr = (ngtcp2_streams_blocked){
    .type = NGTCP2_FRAME_STREAMS_BLOCKED_BIDI,
    .max_streams = 0xF1F2F3F4U,
  };

  rv = ngtcp2_pkt_encode_streams_blocked_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_streams_blocked_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint64(fr.max_streams, ==, nfr.max_streams);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_streams_blocked_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_new_connection_id_frame(void) {
  uint8_t buf[256];
  ngtcp2_new_connection_id fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 4 + 2 + 1 + 18 + sizeof(fr.token.data);
  size_t i;

  fr = (ngtcp2_new_connection_id){
    .type = NGTCP2_FRAME_NEW_CONNECTION_ID,
    .seq = 1000000009,
    .retire_prior_to = 255,
    .cid = make_scid(),
    .token =
      {
        .data = {0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1,
                 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1},
      },
  };

  rv = ngtcp2_pkt_encode_new_connection_id_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_new_connection_id_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint64(fr.seq, ==, nfr.seq);
  assert_true(ngtcp2_cid_eq(&fr.cid, &nfr.cid));
  assert_true(ngtcp2_stateless_reset_token_eq(&fr.token, &nfr.token));

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_new_connection_id_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_stop_sending_frame(void) {
  uint8_t buf[16];
  ngtcp2_stop_sending fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 8 + 4;
  size_t i;

  fr = (ngtcp2_stop_sending){
    .type = NGTCP2_FRAME_STOP_SENDING,
    .stream_id = 0xF1F2F3F4U,
    .app_error_code = 0xE1E2U,
  };

  rv = ngtcp2_pkt_encode_stop_sending_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_stop_sending_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_int64(fr.stream_id, ==, nfr.stream_id);
  assert_uint64(fr.app_error_code, ==, nfr.app_error_code);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_stop_sending_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_path_challenge_frame(void) {
  uint8_t buf[9];
  ngtcp2_path_challenge fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 8;
  size_t i;

  fr = (ngtcp2_path_challenge){
    .type = NGTCP2_FRAME_PATH_CHALLENGE,
    .data =
      {
        .data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
      },
  };

  rv = ngtcp2_pkt_encode_path_challenge_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_path_challenge_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_memory_equal(sizeof(fr.data.data), fr.data.data, nfr.data.data);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_path_challenge_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_path_response_frame(void) {
  uint8_t buf[9];
  ngtcp2_path_response fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1 + 8;
  size_t i;

  fr = (ngtcp2_path_response){
    .type = NGTCP2_FRAME_PATH_RESPONSE,
    .data =
      {
        .data = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
      },
  };

  rv = ngtcp2_pkt_encode_path_response_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_path_response_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_memory_equal(sizeof(fr.data.data), fr.data.data, nfr.data.data);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_path_response_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_crypto_frame(void) {
  static const uint8_t data[] = "0123456789abcdef1";
  uint8_t buf[256];
  ngtcp2_vec datav, ndatav;
  ngtcp2_stream fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen;
  size_t i;

  fr = (ngtcp2_stream){
    .type = NGTCP2_FRAME_CRYPTO,
    .offset = 0x31F2F3F4F5F6F7F8ULL,
    .datacnt = 1,
    .data = &datav,
  };
  datav = (ngtcp2_vec){
    .len = ngtcp2_strlen_lit(data),
    .base = (uint8_t *)data,
  };

  framelen = 1 + 8 + 1 + 17;

  rv = ngtcp2_pkt_encode_crypto_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.data = &ndatav;
  rv = ngtcp2_pkt_decode_crypto_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint8(fr.flags, ==, nfr.flags);
  assert_int(fr.fin, ==, nfr.fin);
  assert_int64(fr.stream_id, ==, nfr.stream_id);
  assert_uint64(fr.offset, ==, nfr.offset);
  assert_size(fr.datacnt, ==, nfr.datacnt);
  assert_size(fr.data[0].len, ==, nfr.data[0].len);
  assert_memory_equal(fr.data[0].len, fr.data[0].base, nfr.data[0].base);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.data = &ndatav;
    rv = ngtcp2_pkt_decode_crypto_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_new_token_frame(void) {
  static const uint8_t token[] = "0123456789abcdef2";
  uint8_t buf[256];
  ngtcp2_new_token fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen;
  size_t i;

  fr = (ngtcp2_new_token){
    .type = NGTCP2_FRAME_NEW_TOKEN,
    .token = (uint8_t *)token,
    .tokenlen = ngtcp2_strlen_lit(token),
  };

  framelen = 1 + 1 + ngtcp2_strlen_lit(token);

  rv = ngtcp2_pkt_encode_new_token_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_new_token_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_size(fr.tokenlen, ==, nfr.tokenlen);
  assert_memory_equal(fr.tokenlen, fr.token, nfr.token);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_new_token_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_retire_connection_id_frame(void) {
  uint8_t buf[256];
  ngtcp2_retire_connection_id fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen;
  size_t i;

  fr = (ngtcp2_retire_connection_id){
    .type = NGTCP2_FRAME_RETIRE_CONNECTION_ID,
    .seq = 1000000007,
  };

  framelen = 1 + ngtcp2_put_uvarintlen(fr.seq);

  rv = ngtcp2_pkt_encode_retire_connection_id_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_retire_connection_id_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_uint64(fr.seq, ==, nfr.seq);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    rv = ngtcp2_pkt_decode_retire_connection_id_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }
}

void test_ngtcp2_pkt_encode_handshake_done_frame(void) {
  uint8_t buf[16];
  ngtcp2_handshake_done fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen = 1;

  fr.type = NGTCP2_FRAME_HANDSHAKE_DONE;

  rv = ngtcp2_pkt_encode_handshake_done_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  rv = ngtcp2_pkt_decode_handshake_done_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
}

void test_ngtcp2_pkt_encode_datagram_frame(void) {
  static const uint8_t data[] = "0123456789abcdef3";
  uint8_t buf[256];
  ngtcp2_vec datav, ndatav;
  ngtcp2_datagram fr, nfr;
  ngtcp2_ssize rv;
  size_t framelen;
  size_t i;

  fr = (ngtcp2_datagram){
    .type = NGTCP2_FRAME_DATAGRAM_LEN,
    .datacnt = 1,
    .data = &datav,
  };
  datav = (ngtcp2_vec){
    .len = ngtcp2_strlen_lit(data),
    .base = (uint8_t *)data,
  };

  framelen = 1 + 1 + 17;

  rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.data = &ndatav;
  rv = ngtcp2_pkt_decode_datagram_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_size(fr.datacnt, ==, nfr.datacnt);
  assert_size(fr.data->len, ==, nfr.data->len);
  assert_memory_equal(fr.data->len, fr.data->base, nfr.data->base);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.data = &ndatav;
    rv = ngtcp2_pkt_decode_datagram_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_datagram){0};

  /* Without length field */
  fr = (ngtcp2_datagram){
    .type = NGTCP2_FRAME_DATAGRAM,
    .datacnt = 1,
    .data = &datav,
  };
  datav = (ngtcp2_vec){
    .len = ngtcp2_strlen_lit(data),
    .base = (uint8_t *)data,
  };

  framelen = 1 + 17;

  rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.data = &ndatav;
  rv = ngtcp2_pkt_decode_datagram_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_size(fr.datacnt, ==, nfr.datacnt);
  assert_size(fr.data->len, ==, nfr.data->len);
  assert_memory_equal(fr.data->len, fr.data->base, nfr.data->base);

  /* Does not fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.data = &ndatav;
    rv = ngtcp2_pkt_decode_datagram_frame(&nfr, buf, i);

    assert_ptrdiff((ngtcp2_ssize)i, ==, rv);
  }

  nfr = (ngtcp2_datagram){0};

  /* Zero length data with length field */
  fr = (ngtcp2_datagram){
    .type = NGTCP2_FRAME_DATAGRAM_LEN,
  };

  framelen = 1 + 1;

  rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.data = &ndatav;
  rv = ngtcp2_pkt_decode_datagram_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_size(fr.datacnt, ==, nfr.datacnt);

  /* Fail if a frame is truncated. */
  for (i = 1; i < framelen; ++i) {
    nfr.data = &ndatav;
    rv = ngtcp2_pkt_decode_datagram_frame(&nfr, buf, i);

    assert_ptrdiff(NGTCP2_ERR_FRAME_ENCODING, ==, rv);
  }

  nfr = (ngtcp2_datagram){0};

  /* Zero length data without length field */
  fr = (ngtcp2_datagram){
    .type = NGTCP2_FRAME_DATAGRAM,
  };

  framelen = 1;

  rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);

  nfr.data = &ndatav;
  rv = ngtcp2_pkt_decode_datagram_frame(&nfr, buf, framelen);

  assert_ptrdiff((ngtcp2_ssize)framelen, ==, rv);
  assert_uint64(fr.type, ==, nfr.type);
  assert_size(fr.datacnt, ==, nfr.datacnt);
}

void test_ngtcp2_pkt_adjust_pkt_num(void) {
  assert_int64(0xAA831F94ULL, ==,
               ngtcp2_pkt_adjust_pkt_num(0xAA82F30EULL, 0x1F94, 2));

  assert_int64(0xFF, ==, ngtcp2_pkt_adjust_pkt_num(0x0100, 0xFF, 1));
  assert_int64(0x01FF, ==, ngtcp2_pkt_adjust_pkt_num(0x01FF, 0xFF, 1));
  assert_int64(0x0FFF, ==, ngtcp2_pkt_adjust_pkt_num(0x1000, 0xFF, 1));
  assert_int64(0x80, ==, ngtcp2_pkt_adjust_pkt_num(0x00, 0x80, 1));
  assert_int64(0x3FFFFFFFFFFFFFABULL, ==,
               ngtcp2_pkt_adjust_pkt_num(NGTCP2_MAX_PKT_NUM, 0xAB, 1));
  assert_int64(0x4000000000000000ULL, ==,
               ngtcp2_pkt_adjust_pkt_num(NGTCP2_MAX_PKT_NUM, 0x00, 1));
  assert_int64(250, ==, ngtcp2_pkt_adjust_pkt_num(255, 250, 1));
  assert_int64(8, ==, ngtcp2_pkt_adjust_pkt_num(50, 8, 1));
  assert_int64(0, ==, ngtcp2_pkt_adjust_pkt_num(-1, 0, 1));
}

void test_ngtcp2_pkt_validate_ack(void) {
  int rv;
  ngtcp2_ack_range ack_ranges[NGTCP2_MAX_ACK_RANGES];
  ngtcp2_ack fr;

  /* too long first_ack_range */
  fr = (ngtcp2_ack){
    .largest_ack = 1,
    .first_ack_range = 2,
  };

  rv = ngtcp2_pkt_validate_ack(&fr, 0);

  assert_ptrdiff(NGTCP2_ERR_ACK_FRAME, ==, rv);

  /* gap is too large */
  fr = (ngtcp2_ack){
    .largest_ack = 250,
    .first_ack_range = 1,
    .rangecnt = 1,
    .ranges = ack_ranges,
  };
  ack_ranges[0] = (ngtcp2_ack_range){
    .gap = 248,
  };

  rv = ngtcp2_pkt_validate_ack(&fr, 0);

  assert_ptrdiff(NGTCP2_ERR_ACK_FRAME, ==, rv);

  /* too large range len */
  fr = (ngtcp2_ack){
    .largest_ack = 250,
    .rangecnt = 1,
    .ranges = ack_ranges,
  };
  ack_ranges[0] = (ngtcp2_ack_range){
    .gap = 248,
    .len = 1,
  };

  rv = ngtcp2_pkt_validate_ack(&fr, 0);

  assert_ptrdiff(NGTCP2_ERR_ACK_FRAME, ==, rv);

  /* first ack range contains packet number that is smaller than the
     minimum. */
  fr = (ngtcp2_ack){
    .largest_ack = 250,
  };

  rv = ngtcp2_pkt_validate_ack(&fr, 251);

  assert_ptrdiff(NGTCP2_ERR_PROTO, ==, rv);

  /* second ack range contains packet number that is smaller than the
     minimum. */
  fr = (ngtcp2_ack){
    .largest_ack = 250,
    .rangecnt = 1,
    .ranges = ack_ranges,
  };
  ack_ranges[0] = (ngtcp2_ack_range){0};

  rv = ngtcp2_pkt_validate_ack(&fr, 249);

  assert_ptrdiff(NGTCP2_ERR_PROTO, ==, rv);
}

void test_ngtcp2_pkt_write_stateless_reset(void) {
  uint8_t buf[256];
  ngtcp2_ssize spktlen;
  uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN];
  uint8_t rand[256];
  size_t i;
  uint8_t *p;
  size_t randlen;

  memset(rand, 0, sizeof(rand));
  for (i = 0; i < NGTCP2_STATELESS_RESET_TOKENLEN; ++i) {
    token[i] = (uint8_t)(i + 1);
  }

  spktlen = ngtcp2_pkt_write_stateless_reset(buf, sizeof(buf), token, rand,
                                             sizeof(rand));

  p = buf;

  assert_ptrdiff(256, ==, spktlen);
  assert_uint8(0, ==, (*p & NGTCP2_HEADER_FORM_BIT));
  assert_true((*p & NGTCP2_FIXED_BIT_MASK));

  ++p;

  randlen = (size_t)(spktlen - (p - buf) - NGTCP2_STATELESS_RESET_TOKENLEN);

  assert_memory_equal(randlen, rand, p);

  p += randlen;

  assert_memory_equal(NGTCP2_STATELESS_RESET_TOKENLEN, token, p);

  p += NGTCP2_STATELESS_RESET_TOKENLEN;

  assert_ptrdiff(spktlen, ==, p - buf);

  /* Not enough buffer */
  spktlen = ngtcp2_pkt_write_stateless_reset(
    buf,
    NGTCP2_MIN_STATELESS_RESET_RANDLEN - 1 + NGTCP2_STATELESS_RESET_TOKENLEN,
    token, rand, sizeof(rand));

  assert_ptrdiff(NGTCP2_ERR_NOBUF, ==, spktlen);
}

void test_ngtcp2_pkt_write_stateless_reset2(void) {
  uint8_t buf[256];
  ngtcp2_ssize spktlen;
  static const ngtcp2_stateless_reset_token token =
    make_stateless_reset_token();
  static const uint8_t rand[256] = {0xDE, 0xAD, 0xF0, 0x0D};
  uint8_t *p;
  size_t randlen;

  spktlen = ngtcp2_pkt_write_stateless_reset2(buf, sizeof(buf), &token, rand,
                                              sizeof(rand));

  p = buf;

  assert_ptrdiff(256, ==, spktlen);
  assert_uint8(0, ==, (*p & NGTCP2_HEADER_FORM_BIT));
  assert_true((*p & NGTCP2_FIXED_BIT_MASK));
  assert_uint8(0x40U | (0x3FU & rand[0]), ==, *p);

  ++p;

  randlen = (size_t)(spktlen - (p - buf)) - sizeof(token.data);

  assert_memory_equal(randlen, rand + 1, p);

  p += randlen;

  assert_memory_equal(sizeof(token.data), token.data, p);

  p += sizeof(token.data);

  assert_ptrdiff(spktlen, ==, p - buf);

  /* Not enough buffer */
  spktlen = ngtcp2_pkt_write_stateless_reset2(
    buf, NGTCP2_MIN_STATELESS_RESET_RANDLEN - 1 + sizeof(token.data), &token,
    rand, sizeof(rand));

  assert_ptrdiff(NGTCP2_ERR_NOBUF, ==, spktlen);
}

void test_ngtcp2_pkt_write_retry(void) {
  uint8_t buf[256];
  ngtcp2_ssize spktlen;
  static const ngtcp2_cid scid = make_scid();
  static const ngtcp2_cid dcid = make_dcid();
  static const ngtcp2_cid odcid = make_rcid();
  ngtcp2_pkt_hd nhd;
  uint8_t token[32];
  size_t i;
  ngtcp2_pkt_retry retry;
  ngtcp2_ssize nread;
  int rv;
  static const ngtcp2_crypto_aead aead = {0};
  static const ngtcp2_crypto_aead_ctx aead_ctx = {0};
  static const uint8_t tag[NGTCP2_RETRY_TAGLEN] = {0};

  for (i = 0; i < sizeof(token); ++i) {
    token[i] = (uint8_t)i;
  }

  spktlen = ngtcp2_pkt_write_retry(buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &dcid,
                                   &scid, &odcid, token, sizeof(token),
                                   null_retry_encrypt, &aead, &aead_ctx);

  assert_ptrdiff(0, <, spktlen);

  nhd = (ngtcp2_pkt_hd){0};

  nread = ngtcp2_pkt_decode_hd_long(&nhd, buf, (size_t)spktlen);

  assert_ptrdiff(0, <, nread);
  assert_uint8(NGTCP2_PKT_RETRY, ==, nhd.type);
  assert_uint32(NGTCP2_PROTO_VER_V1, ==, nhd.version);
  assert_true(ngtcp2_cid_eq(&dcid, &nhd.dcid));
  assert_true(ngtcp2_cid_eq(&scid, &nhd.scid));

  rv = ngtcp2_pkt_decode_retry(&retry, buf + nread, (size_t)(spktlen - nread));

  assert_int(0, ==, rv);
  assert_size(sizeof(token), ==, retry.tokenlen);
  assert_memory_equal(sizeof(token), token, retry.token);
  assert_memory_equal(sizeof(tag), tag, retry.tag);
}

void test_ngtcp2_pkt_write_version_negotiation(void) {
  uint8_t buf[256];
  ngtcp2_ssize spktlen;
  static const uint32_t sv[] = {0xF1F2F3F4, 0x1F2F3F4F};
  const uint8_t *p;
  size_t i;
  static const ngtcp2_cid dcid = make_dcid();
  static const ngtcp2_cid scid = make_scid();
  uint32_t v;

  spktlen = ngtcp2_pkt_write_version_negotiation(
    buf, sizeof(buf), 133, dcid.data, dcid.datalen, scid.data, scid.datalen, sv,
    ngtcp2_arraylen(sv));

  assert_ptrdiff((ngtcp2_ssize)(1 + 4 + 1 + dcid.datalen + 1 + scid.datalen +
                                ngtcp2_arraylen(sv) * 4),
                 ==, spktlen);

  p = buf;

  assert_uint8((0xC0U | 133), ==, buf[0]);

  ++p;

  p = ngtcp2_get_uint32be(&v, p);

  assert_uint32(0, ==, v);

  assert_size(dcid.datalen, ==, *p);

  ++p;

  assert_memory_equal(dcid.datalen, dcid.data, p);

  p += dcid.datalen;

  assert_size(scid.datalen, ==, *p);

  ++p;

  assert_memory_equal(scid.datalen, scid.data, p);

  p += scid.datalen;

  for (i = 0; i < ngtcp2_arraylen(sv); ++i) {
    p = ngtcp2_get_uint32be(&v, p);

    assert_uint32(sv[i], ==, v);
  }
}

void test_ngtcp2_pkt_stream_max_datalen(void) {
  size_t len;

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 0, 2);

  assert_size((size_t)-1, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 0, 3);

  assert_size(0, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 1, 3);

  assert_size(0, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 1, 4);

  assert_size(1, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 1, 1, 4);

  assert_size(0, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 63, 66);

  assert_size(63, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 63, 65);

  assert_size(62, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 1396, 1400);

  assert_size(1396, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 1396, 1399);

  assert_size(1395, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 1396, 9);

  assert_size(6, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 16385, 16391);

  assert_size(16385, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 16385, 16390);

  assert_size(16384, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 1073741824, 1073741834);

  assert_size(1073741824, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 1073741824, 1073741833);

  assert_size(1073741823, ==, len);

  len = ngtcp2_pkt_stream_max_datalen(63, 0, 16383, 16387);

  assert_size(16383, ==, len);
}

void test_ngtcp2_pkt_split_vec_rand(void) {
  ngtcp2_pcg32 pcg;
  ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT];
  uint64_t offsets[NGTCP2_MAX_STREAM_DATACNT];
  size_t datacnt;

  ngtcp2_pcg32_init(&pcg, 0);

  data[0] = (ngtcp2_vec){
    .base = null_data,
    .len = 4096,
  };
  offsets[0] = 1000000007;

  datacnt = ngtcp2_pkt_split_vec_rand(data, 1, offsets, &pcg, 3);

  assert_size(4, ==, datacnt);

  assert_size(1024, ==, data[0].len);
  assert_ptr_equal(null_data, data[0].base);

  assert_size(1024, ==, data[1].len);
  assert_ptr_equal(null_data + 2048, data[1].base);

  assert_size(1024, ==, data[2].len);
  assert_ptr_equal(null_data + 1024, data[2].base);

  assert_size(1024, ==, data[3].len);
  assert_ptr_equal(null_data + 3072, data[3].base);

  assert_uint64(1000000007, ==, offsets[0]);
  assert_uint64(1000000007 + 2048, ==, offsets[1]);
  assert_uint64(1000000007 + 1024, ==, offsets[2]);
  assert_uint64(1000000007 + 3072, ==, offsets[3]);

  ngtcp2_pcg32_init(&pcg, 1);

  data[0] = (ngtcp2_vec){
    .base = null_data,
    .len = 4096,
  };
  offsets[0] = 1000000007;

  datacnt = ngtcp2_pkt_split_vec_rand(data, 1, offsets, &pcg, 3);

  assert_size(4, ==, datacnt);

  assert_size(512, ==, data[0].len);
  assert_ptr_equal(null_data, data[0].base);

  assert_size(2048, ==, data[1].len);
  assert_ptr_equal(null_data + 2048, data[1].base);

  assert_size(1024, ==, data[2].len);
  assert_ptr_equal(null_data + 1024, data[2].base);

  assert_size(512, ==, data[3].len);
  assert_ptr_equal(null_data + 512, data[3].base);

  assert_uint64(1000000007, ==, offsets[0]);
  assert_uint64(1000000007 + 2048, ==, offsets[1]);
  assert_uint64(1000000007 + 1024, ==, offsets[2]);
  assert_uint64(1000000007 + 512, ==, offsets[3]);
}

void test_ngtcp2_pkt_split_vec_at(void) {
  ngtcp2_vec data[2] = {
    {
      .base = null_data,
      .len = 4096,
    },
  };
  uint64_t offsets[2] = {
    1000000007,
  };
  size_t datacnt;

  datacnt = ngtcp2_pkt_split_vec_at(data, 1, offsets, 111);

  assert_size(2, ==, datacnt);

  assert_size(111, ==, data[0].len);
  assert_ptr_equal(null_data, data[0].base);

  assert_size(4096 - 111, ==, data[1].len);
  assert_ptr_equal(null_data + 111, data[1].base);

  assert_uint64(1000000007, ==, offsets[0]);
  assert_uint64(1000000007 + 111, ==, offsets[1]);
}

void test_ngtcp2_pkt_find_server_name(void) {
  uint8_t rawbuf[4096] = {0};
  ngtcp2_buf buf;
  int rv;
  ngtcp2_vec data;
  ngtcp2_vec server_name;
  size_t i;
  uint8_t *p;

  ngtcp2_buf_init(&buf, rawbuf, sizeof(rawbuf));

  /* msg_type */
  *buf.last++ = 1;
  /* length */
  buf.last = ngtcp2_put_uint24be(buf.last, 1000);
  /* legacy_version */
  buf.last = ngtcp2_put_uint16be(buf.last, 0x0303);
  /* random */
  buf.last += 32;
  /* legacy_session_id */
  *buf.last++ = 23;
  buf.last += 23;
  /* cipher_suites */
  buf.last = ngtcp2_put_uint16be(buf.last, 125);
  buf.last += 125;
  /* legacy_compression_methods */
  *buf.last++ = 7;
  buf.last += 7;
  /* extensions */
  buf.last = ngtcp2_put_uint16be(buf.last, 400);
  /* extension 1 */
  buf.last = ngtcp2_put_uint16be(buf.last, 999);
  buf.last = ngtcp2_put_uint16be(buf.last, 120);
  buf.last += 120;
  /* extension 2 */
  buf.last = ngtcp2_put_uint16be(buf.last, 65530);
  buf.last = ngtcp2_put_uint16be(buf.last, 0);
  /* server_name extension */
  buf.last = ngtcp2_put_uint16be(buf.last, 0);
  buf.last = ngtcp2_put_uint16be(buf.last, 262);
  /* server_name_list */
  buf.last = ngtcp2_put_uint16be(buf.last, 260);
  /* name_type */
  *buf.last++ = 0;
  /* name */
  buf.last = ngtcp2_put_uint16be(buf.last, 257);
  buf.last += 257;

  data = (ngtcp2_vec){
    .base = buf.pos,
    .len = 1200,
  };

  rv = ngtcp2_pkt_find_server_name(&server_name, &data);

  assert_int(1, ==, rv);
  assert_size(257, ==, server_name.len);
  assert_ptr_equal(buf.pos + 1 + 3 + 2 + 32 + 1 + 23 + 2 + 125 + 1 + 7 + 2 + 2 +
                     2 + 120 + 2 + 2 + 2 + 2 + 2 + 1 + 2,
                   server_name.base);

  for (i = 1; i < ngtcp2_buf_len(&buf) + 1; ++i) {
    p = malloc(i);

    memcpy(p, buf.pos, i);

    data = (ngtcp2_vec){
      .base = p,
      .len = i,
    };

    rv = ngtcp2_pkt_find_server_name(&server_name, &data);

    free(p);

    if (i == ngtcp2_buf_len(&buf)) {
      assert_true(rv);
      assert_size(257, ==, server_name.len);
    } else {
      assert_false(rv);
    }
  }
}

void test_ngtcp2_pkt_append_ping_and_padding(void) {
  ngtcp2_pcg32 pcg;
  ngtcp2_vec data[NGTCP2_MAX_STREAM_DATACNT];
  size_t datacnt;

  ngtcp2_pcg32_init(&pcg, 1000000009);

  data[0] = (ngtcp2_vec){
    .base = null_data,
    .len = 999,
  };

  datacnt = ngtcp2_pkt_append_ping_and_padding(data, 1, &pcg, 19);

  assert_size(7, ==, datacnt);
  assert_ptr_equal(null_data, data[0].base);
  assert_size(999, ==, data[0].len);
  /* PADDING len = 2 */
  assert_size(2, ==, data[1].len);
  assert_null(data[1].base);
  /* PADDING len = 5 */
  assert_size(5, ==, data[2].len);
  assert_null(data[2].base);
  /* PADDING len = 1 */
  assert_size(1, ==, data[3].len);
  assert_null(data[3].base);
  /* PADDING len = 6 */
  assert_size(6, ==, data[4].len);
  assert_null(data[4].base);
  /* PADDING len = 2 */
  assert_size(2, ==, data[5].len);
  assert_null(data[5].base);
  /* PADDING len = 3 */
  assert_size(3, ==, data[6].len);
  assert_null(data[6].base);

  /* Stop adding frames because the array gets full. */
  ngtcp2_pcg32_init(&pcg, 1000000009);

  datacnt = ngtcp2_pkt_append_ping_and_padding(
    data, NGTCP2_MAX_STREAM_DATACNT - 1, &pcg, 19);

  assert_size(NGTCP2_MAX_STREAM_DATACNT, ==, datacnt);
  /* PADDING len = 2 */
  assert_size(2, ==, data[NGTCP2_MAX_STREAM_DATACNT - 1].len);
  assert_null(data[NGTCP2_MAX_STREAM_DATACNT - 1].base);

  /* The array is already full. */
  ngtcp2_pcg32_init(&pcg, 1000000009);

  datacnt = ngtcp2_pkt_append_ping_and_padding(data, NGTCP2_MAX_STREAM_DATACNT,
                                               &pcg, 19);

  assert_size(NGTCP2_MAX_STREAM_DATACNT, ==, datacnt);
}

void test_ngtcp2_pkt_permutate_vec(void) {
  ngtcp2_pcg32 pcg;
  ngtcp2_vec data[] = {
    {
      .base = null_data,
      .len = 1,
    },
    {
      .base = null_data + 1,
      .len = 2,
    },
    {
      .base = null_data + 1 + 2,
      .len = 3,
    },
    {
      .base = null_data + 1 + 2 + 3,
      .len = 4,
    },
  };
  uint64_t offsets[] = {
    0,
    1,
    3,
    6,
  };

  ngtcp2_pcg32_init(&pcg, 231);

  ngtcp2_pkt_permutate_vec(data, ngtcp2_arraylen(data), offsets, &pcg);

  assert_size(4, ==, data[0].len);
  assert_ptr_equal(null_data + 6, data[0].base);

  assert_size(1, ==, data[1].len);
  assert_ptr_equal(null_data, data[1].base);

  assert_size(2, ==, data[2].len);
  assert_ptr_equal(null_data + 1, data[2].base);

  assert_size(3, ==, data[3].len);
  assert_ptr_equal(null_data + 3, data[3].base);

  assert_uint64(6, ==, offsets[0]);
  assert_uint64(0, ==, offsets[1]);
  assert_uint64(1, ==, offsets[2]);
  assert_uint64(3, ==, offsets[3]);
}

void test_ngtcp2_pkt_remove_vec_partial(void) {
  ngtcp2_pcg32 pcg;
  ngtcp2_vec data[2];
  uint64_t offsets[2];
  ngtcp2_vec removed;
  ngtcp2_vec partial;
  size_t datacnt;

  ngtcp2_pcg32_init(&pcg, 0);

  data[0] = (ngtcp2_vec){
    .base = null_data,
    .len = 1777,
  };
  offsets[0] = 551;

  partial = (ngtcp2_vec){
    .base = null_data + 111,
    .len = 50,
  };

  datacnt =
    ngtcp2_pkt_remove_vec_partial(&removed, data, 1, offsets, &pcg, &partial);

  assert_size(datacnt, ==, 2);

  assert_size(136, ==, data[0].len);
  assert_ptr_equal(null_data, data[0].base);

  assert_size(28, ==, removed.len);
  assert_ptr_equal(null_data + 136, removed.base);

  assert_size(1613, ==, data[1].len);
  assert_ptr_equal(null_data + 164, data[1].base);

  assert_uint64(offsets[0], ==, 551);
  assert_uint64(offsets[1], ==, 551 + 164);

  /* No ngtcp2_vec appended */
  ngtcp2_pcg32_init(&pcg, 0);

  data[0] = (ngtcp2_vec){
    .base = null_data,
    .len = 1777,
  };
  offsets[0] = 88;

  partial = (ngtcp2_vec){
    .base = null_data + 1775,
    .len = 2,
  };

  datacnt =
    ngtcp2_pkt_remove_vec_partial(&removed, data, 1, offsets, &pcg, &partial);

  assert_size(datacnt, ==, 1);

  assert_size(1776, ==, data[0].len);
  assert_ptr_equal(null_data, data[0].base);

  assert_size(1, ==, removed.len);
  assert_ptr_equal(null_data + 1776, removed.base);

  assert_uint64(offsets[0], ==, 88);
}

void test_ngtcp2_stateless_reset_token_eq(void) {
  {
    static const ngtcp2_stateless_reset_token a = make_stateless_reset_token();
    static const ngtcp2_stateless_reset_token b = make_stateless_reset_token();

    assert_true(ngtcp2_stateless_reset_token_eq(&a, &b));
  }

  {
    static const ngtcp2_stateless_reset_token a = make_stateless_reset_token();
    ngtcp2_stateless_reset_token b = make_stateless_reset_token();

    b.data[sizeof(b.data) - 1] = 0;

    assert_false(ngtcp2_stateless_reset_token_eq(&a, &b));
  }
}
