/** @file
 *
 *  A brief file description
 *
 *  @section license License
 *
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

#include "catch.hpp"

#include "quic/Mock.h"
#include "quic/QUICFrame.h"
#include "quic/QUICStream.h"

extern const ink_freelist_ops *freelist_global_ops;
extern const ink_freelist_ops *freelist_class_ops;

TEST_CASE("QUICFrame Type", "[quic]")
{
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x00")) == QUICFrameType::PADDING);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x01")) == QUICFrameType::PING);

  // Range of ACK
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x02")) == QUICFrameType::ACK);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x03")) == QUICFrameType::ACK);

  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x04")) == QUICFrameType::RESET_STREAM);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x05")) == QUICFrameType::STOP_SENDING);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x06")) == QUICFrameType::CRYPTO);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x07")) == QUICFrameType::NEW_TOKEN);

  // Range of STREAM
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x08")) == QUICFrameType::STREAM);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x0f")) == QUICFrameType::STREAM);

  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x10")) == QUICFrameType::MAX_DATA);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x11")) == QUICFrameType::MAX_STREAM_DATA);

  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x12")) == QUICFrameType::MAX_STREAMS);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x13")) == QUICFrameType::MAX_STREAMS);

  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x14")) == QUICFrameType::DATA_BLOCKED);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x15")) == QUICFrameType::STREAM_DATA_BLOCKED);

  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x16")) == QUICFrameType::STREAMS_BLOCKED);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x17")) == QUICFrameType::STREAMS_BLOCKED);

  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x18")) == QUICFrameType::NEW_CONNECTION_ID);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x19")) == QUICFrameType::RETIRE_CONNECTION_ID);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x1a")) == QUICFrameType::PATH_CHALLENGE);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x1b")) == QUICFrameType::PATH_RESPONSE);

  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x1c")) == QUICFrameType::CONNECTION_CLOSE);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x1d")) == QUICFrameType::CONNECTION_CLOSE);

  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x1e")) == QUICFrameType::HANDSHAKE_DONE);

  // Undefined range
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\x1f")) == QUICFrameType::UNKNOWN);
  CHECK(QUICFrame::type(reinterpret_cast<const uint8_t *>("\xff")) == QUICFrameType::UNKNOWN);
}

TEST_CASE("Load STREAM Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];

  SECTION("OLF=000")
  {
    uint8_t buf1[] = {
      0x08,                   // 0b00001OLF (OLF=000)
      0x01,                   // Stream ID
      0x01, 0x02, 0x03, 0x04, // Stream Data
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::STREAM);
    CHECK(frame1->size() == 6);
    const QUICStreamFrame *stream_frame = static_cast<const QUICStreamFrame *>(frame1);
    CHECK(stream_frame->stream_id() == 0x01);
    CHECK(stream_frame->offset() == 0x00);
    CHECK(stream_frame->data_length() == 4);
    CHECK(memcmp(stream_frame->data()->start(), "\x01\x02\x03\x04", 4) == 0);
    CHECK(stream_frame->has_fin_flag() == false);
  }

  SECTION("OLF=010")
  {
    uint8_t buf1[] = {
      0x0a,                         // 0b00001OLF (OLF=010)
      0x01,                         // Stream ID
      0x05,                         // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05, // Stream Data
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::STREAM);
    CHECK(frame1->size() == 8);
    const QUICStreamFrame *stream_frame = static_cast<const QUICStreamFrame *>(frame1);
    CHECK(stream_frame->stream_id() == 0x01);
    CHECK(stream_frame->offset() == 0x00);
    CHECK(stream_frame->data_length() == 5);
    CHECK(memcmp(stream_frame->data()->start(), "\x01\x02\x03\x04\x05", 5) == 0);
    CHECK(stream_frame->has_fin_flag() == false);
  }

  SECTION("OLF=110")
  {
    uint8_t buf1[] = {
      0x0e,                         // 0b00001OLF (OLF=110)
      0x01,                         // Stream ID
      0x02,                         // Data Length
      0x05,                         // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05, // Stream Data
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::STREAM);
    CHECK(frame1->size() == 9);

    const QUICStreamFrame *stream_frame = static_cast<const QUICStreamFrame *>(frame1);
    CHECK(stream_frame->stream_id() == 0x01);
    CHECK(stream_frame->offset() == 0x02);
    CHECK(stream_frame->data_length() == 5);
    CHECK(memcmp(stream_frame->data()->start(), "\x01\x02\x03\x04\x05", 5) == 0);
    CHECK(stream_frame->has_fin_flag() == false);
  }

  SECTION("OLF=111")
  {
    uint8_t buf1[] = {
      0x0f,                         // 0b00001OLF (OLF=111)
      0x01,                         // Stream ID
      0x02,                         // Data Length
      0x05,                         // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05, // Stream Data
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::STREAM);
    CHECK(frame1->size() == 9);

    const QUICStreamFrame *stream_frame = static_cast<const QUICStreamFrame *>(frame1);
    CHECK(stream_frame->stream_id() == 0x01);
    CHECK(stream_frame->offset() == 0x02);
    CHECK(stream_frame->data_length() == 5);
    CHECK(memcmp(stream_frame->data()->start(), "\x01\x02\x03\x04\x05", 5) == 0);
    CHECK(stream_frame->has_fin_flag() == true);
  }

  SECTION("BAD DATA")
  {
    uint8_t buf1[] = {
      0x0e,                   // 0b00001OLF (OLF=110)
      0x01,                   // Stream ID
      0x02,                   // Data Length
      0x05,                   // Data Length
      0x01, 0x02, 0x03, 0x04, // BAD Stream Data
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::STREAM);
    CHECK(frame1->valid() == false);
  }

  SECTION("BAD DATA")
  {
    uint8_t buf1[] = {
      0x0e, // 0b00001OLF (OLF=110)
      0x01, // Stream ID
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::STREAM);
    CHECK(frame1->valid() == false);
  }
}

TEST_CASE("Store STREAM Frame", "[quic]")
{
  SECTION("8bit stream id, 0bit offset")
  {
    uint8_t buf[32]     = {0};
    size_t len          = 0;
    uint8_t expected1[] = {
      0x0a,                         // 0b00001OLF (OLF=010)
      0x01,                         // Stream ID
      0x05,                         // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05, // Stream Data
    };

    uint8_t raw1[]           = "\x01\x02\x03\x04\x05";
    Ptr<IOBufferBlock> block = make_ptr<IOBufferBlock>(new_IOBufferBlock());
    block->alloc(BUFFER_SIZE_INDEX_32K);
    memcpy(block->start(), raw1, 5);
    block->fill(5);

    QUICStreamFrame stream_frame(block, 0x01, 0x00, false, false, true);
    CHECK(stream_frame.size() == 8);

    Ptr<IOBufferBlock> ibb = stream_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == 8);
    CHECK(memcmp(buf, expected1, len) == 0);
  }

  SECTION("8bit stream id, 16bit offset")
  {
    uint8_t buf[32]     = {0};
    size_t len          = 0;
    uint8_t expected2[] = {
      0x0e,                         // 0b00001OLF (OLF=110)
      0x01,                         // Stream ID
      0x01,                         // Offset
      0x05,                         // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05, // Stream Data
    };
    uint8_t raw2[]           = "\x01\x02\x03\x04\x05";
    Ptr<IOBufferBlock> block = make_ptr<IOBufferBlock>(new_IOBufferBlock());
    block->alloc(BUFFER_SIZE_INDEX_32K);
    memcpy(block->start(), raw2, 5);
    block->fill(5);

    QUICStreamFrame stream_frame(block, 0x01, 0x01);
    CHECK(stream_frame.size() == 9);

    Ptr<IOBufferBlock> ibb = stream_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == 9);
    CHECK(memcmp(buf, expected2, len) == 0);
  }

  SECTION("8bit stream id, 32bit offset")
  {
    uint8_t buf[32]     = {0};
    size_t len          = 0;
    uint8_t expected3[] = {
      0x0e,                         // 0b00001OLF (OLF=110)
      0x01,                         // Stream ID
      0x80, 0x01, 0x00, 0x00,       // Offset
      0x05,                         // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05, // Stream Data
    };
    uint8_t raw3[]           = "\x01\x02\x03\x04\x05";
    Ptr<IOBufferBlock> block = make_ptr<IOBufferBlock>(new_IOBufferBlock());
    block->alloc(BUFFER_SIZE_INDEX_32K);
    memcpy(block->start(), raw3, 5);
    block->fill(5);

    QUICStreamFrame stream_frame(block, 0x01, 0x010000);
    CHECK(stream_frame.size() == 12);

    Ptr<IOBufferBlock> ibb = stream_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == 12);
    CHECK(memcmp(buf, expected3, len) == 0);
  }

  SECTION("8bit stream id, 64bit offset")
  {
    uint8_t buf[32]     = {0};
    size_t len          = 0;
    uint8_t expected4[] = {
      0x0e,                                           // 0b00001OLF (OLF=110)
      0x01,                                           // Stream ID
      0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // Offset
      0x05,                                           // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05,                   // Stream Data
    };
    uint8_t raw4[]           = "\x01\x02\x03\x04\x05";
    Ptr<IOBufferBlock> block = make_ptr<IOBufferBlock>(new_IOBufferBlock());
    block->alloc(BUFFER_SIZE_INDEX_32K);
    memcpy(block->start(), raw4, 5);
    block->fill(5);

    QUICStreamFrame stream_frame(block, 0x01, 0x0100000000);
    CHECK(stream_frame.size() == 16);

    Ptr<IOBufferBlock> ibb = stream_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == 16);
    CHECK(memcmp(buf, expected4, len) == 0);
  }

  SECTION("16bit stream id, 64bit offset")
  {
    uint8_t buf[32]     = {0};
    size_t len          = 0;
    uint8_t expected5[] = {
      0x0e,                                           // 0b00001OLF (OLF=110)
      0x41, 0x00,                                     // Stream ID
      0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // Offset
      0x05,                                           // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05,                   // Stream Data
    };
    uint8_t raw5[]           = "\x01\x02\x03\x04\x05";
    Ptr<IOBufferBlock> block = make_ptr<IOBufferBlock>(new_IOBufferBlock());
    block->alloc(BUFFER_SIZE_INDEX_32K);
    memcpy(block->start(), raw5, 5);
    block->fill(5);

    QUICStreamFrame stream_frame(block, 0x0100, 0x0100000000);
    CHECK(stream_frame.size() == 17);

    Ptr<IOBufferBlock> ibb = stream_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == 17);
    CHECK(memcmp(buf, expected5, len) == 0);
  }

  SECTION("24bit stream id, 64bit offset")
  {
    uint8_t buf[32]     = {0};
    size_t len          = 0;
    uint8_t expected6[] = {
      0x0e,                                           // 0b00001OLF (OLF=110)
      0x80, 0x01, 0x00, 0x00,                         // Stream ID
      0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // Offset
      0x05,                                           // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05,                   // Stream Data
    };
    uint8_t raw6[]           = "\x01\x02\x03\x04\x05";
    Ptr<IOBufferBlock> block = make_ptr<IOBufferBlock>(new_IOBufferBlock());
    block->alloc(BUFFER_SIZE_INDEX_32K);
    memcpy(block->start(), raw6, 5);
    block->fill(5);

    QUICStreamFrame stream_frame(block, 0x010000, 0x0100000000);
    CHECK(stream_frame.size() == 19);

    Ptr<IOBufferBlock> ibb = stream_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == 19);
    CHECK(memcmp(buf, expected6, len) == 0);
  }

  SECTION("32bit stream id, 64bit offset")
  {
    uint8_t buf[32]     = {0};
    size_t len          = 0;
    uint8_t expected7[] = {
      0x0e,                                           // 0b00001OLF (OLF=110)
      0x81, 0x00, 0x00, 0x00,                         // Stream ID
      0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // Offset
      0x05,                                           // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05,                   // Stream Data
    };
    uint8_t raw7[]           = "\x01\x02\x03\x04\x05";
    Ptr<IOBufferBlock> block = make_ptr<IOBufferBlock>(new_IOBufferBlock());
    block->alloc(BUFFER_SIZE_INDEX_32K);
    memcpy(block->start(), raw7, 5);
    block->fill(5);

    QUICStreamFrame stream_frame(block, 0x01000000, 0x0100000000);
    CHECK(stream_frame.size() == 19);

    Ptr<IOBufferBlock> ibb = stream_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == 19);
    CHECK(memcmp(buf, expected7, len) == 0);
  }

  SECTION("32bit stream id, 64bit offset, FIN bit")
  {
    uint8_t buf[32]    = {0};
    size_t len         = 0;
    uint8_t expected[] = {
      0x0f,                                           // 0b00001OLF (OLF=111)
      0x81, 0x00, 0x00, 0x00,                         // Stream ID
      0xc0, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // Offset
      0x05,                                           // Data Length
      0x01, 0x02, 0x03, 0x04, 0x05,                   // Stream Data
    };
    uint8_t raw[]            = "\x01\x02\x03\x04\x05";
    Ptr<IOBufferBlock> block = make_ptr<IOBufferBlock>(new_IOBufferBlock());
    block->alloc(BUFFER_SIZE_INDEX_32K);
    memcpy(block->start(), raw, 5);
    block->fill(5);

    QUICStreamFrame stream_frame(block, 0x01000000, 0x0100000000, true);
    CHECK(stream_frame.size() == 19);

    Ptr<IOBufferBlock> ibb = stream_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == 19);
    CHECK(memcmp(buf, expected, len) == 0);
  }
}

TEST_CASE("CRYPTO Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("Loading")
  {
    uint8_t buf[] = {
      0x06,                         // Type
      0x80, 0x01, 0x00, 0x00,       // Offset
      0x05,                         // Length
      0x01, 0x02, 0x03, 0x04, 0x05, // Crypto Data
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::CRYPTO);
    CHECK(frame->size() == sizeof(buf));

    const QUICCryptoFrame *crypto_frame = static_cast<const QUICCryptoFrame *>(frame);
    CHECK(crypto_frame->offset() == 0x010000);
    CHECK(crypto_frame->data_length() == 5);
    CHECK(memcmp(crypto_frame->data()->start(), "\x01\x02\x03\x04\x05", 5) == 0);
  }

  SECTION("BAD Loading")
  {
    uint8_t buf[] = {
      0x06,                   // Type
      0x80, 0x01, 0x00, 0x00, // Offset
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::CRYPTO);
    CHECK(frame->valid() == false);
  }

  SECTION("Storing")
  {
    uint8_t buf[32]    = {0};
    size_t len         = 0;
    uint8_t expected[] = {
      0x06,                         // Type
      0x80, 0x01, 0x00, 0x00,       // Offset
      0x05,                         // Length
      0x01, 0x02, 0x03, 0x04, 0x05, // Crypto Data
    };
    uint8_t raw_data[]       = "\x01\x02\x03\x04\x05";
    Ptr<IOBufferBlock> block = make_ptr<IOBufferBlock>(new_IOBufferBlock());
    block->alloc(BUFFER_SIZE_INDEX_32K);
    memcpy(block->start(), raw_data, 5);
    block->fill(5);

    QUICCryptoFrame crypto_frame(block, 0x010000);
    CHECK(crypto_frame.size() == sizeof(expected));

    Ptr<IOBufferBlock> ibb = crypto_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == sizeof(expected));
    CHECK(memcmp(buf, expected, sizeof(expected)) == 0);
  }
}

TEST_CASE("Load Ack Frame 1", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("0 Ack Block, 8 bit packet number length, 8 bit block length")
  {
    uint8_t buf1[] = {
      0x02,       // Type
      0x12,       // Largest Acknowledged
      0x74, 0x56, // Ack Delay
      0x00,       // Ack Block Count
      0x00,       // Ack Block Section
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::ACK);
    CHECK(frame1->size() == 6);
    const QUICAckFrame *ack_frame1 = static_cast<const QUICAckFrame *>(frame1);
    CHECK(ack_frame1 != nullptr);
    CHECK(ack_frame1->ack_block_count() == 0);
    CHECK(ack_frame1->largest_acknowledged() == 0x12);
    CHECK(ack_frame1->ack_delay() == 0x3456);
    frame1->~QUICFrame();
  }

  SECTION("0 Ack Block, 8 bit packet number length, 8 bit block length")
  {
    uint8_t buf1[] = {
      0x02,                   // Type
      0x80, 0x00, 0x00, 0x01, // Largest Acknowledged
      0x41, 0x71,             // Ack Delay
      0x00,                   // Ack Block Count
      0x80, 0x00, 0x00, 0x01, // Ack Block Section (First ACK Block Length)
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::ACK);
    CHECK(frame1->size() == 12);

    const QUICAckFrame *ack_frame1 = static_cast<const QUICAckFrame *>(frame1);
    CHECK(ack_frame1 != nullptr);
    CHECK(ack_frame1->largest_acknowledged() == 0x01);
    CHECK(ack_frame1->ack_delay() == 0x0171);
    CHECK(ack_frame1->ack_block_count() == 0);
    CHECK(ack_frame1->ecn_section() == nullptr);

    const QUICAckFrame::AckBlockSection *section = ack_frame1->ack_block_section();
    CHECK(section->first_ack_block() == 0x01);
    frame1->~QUICFrame();
  }

  SECTION("2 Ack Block, 8 bit packet number length, 8 bit block length")
  {
    uint8_t buf1[] = {
      0x02,                                           // Type
      0x12,                                           // Largest Acknowledged
      0x74, 0x56,                                     // Ack Delay
      0x02,                                           // Ack Block Count
      0x01,                                           // Ack Block Section (First ACK Block Length)
      0x02,                                           // Ack Block Section (Gap 1)
      0x43, 0x04,                                     // Ack Block Section (ACK Block 1 Length)
      0x85, 0x06, 0x07, 0x08,                         // Ack Block Section (Gap 2)
      0xc9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, // Ack Block Section (ACK Block 2 Length)
    };

    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::ACK);
    CHECK(frame1->size() == 21);
    const QUICAckFrame *ack_frame1 = static_cast<const QUICAckFrame *>(frame1);
    CHECK(ack_frame1 != nullptr);
    CHECK(ack_frame1->largest_acknowledged() == 0x12);
    CHECK(ack_frame1->ack_delay() == 0x3456);
    CHECK(ack_frame1->ack_block_count() == 2);
    CHECK(ack_frame1->ecn_section() == nullptr);

    const QUICAckFrame::AckBlockSection *section = ack_frame1->ack_block_section();
    CHECK(section->first_ack_block() == 0x01);
    auto ite = section->begin();
    CHECK(ite != section->end());
    CHECK(ite->gap() == 0x02);
    CHECK(ite->length() == 0x0304);
    ++ite;
    CHECK(ite != section->end());
    CHECK(ite->gap() == 0x05060708);
    CHECK(ite->length() == 0x090a0b0c0d0e0f10);
    ++ite;
    CHECK(ite == section->end());
    frame1->~QUICFrame();
  }

  SECTION("load bad frame")
  {
    uint8_t buf1[] = {
      0x02, // Type
      0x12, // Largest Acknowledged
    };

    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::ACK);
    CHECK(frame1->valid() == false);
    frame1->~QUICFrame();
  }

  SECTION("load bad block")
  {
    uint8_t buf1[] = {
      0x02,                   // Type
      0x12,                   // Largest Acknowledged
      0x74, 0x56,             // Ack Delay
      0x02,                   // Ack Block Count
      0x01,                   // Ack Block Section (First ACK Block Length)
      0x02,                   // Ack Block Section (Gap 1)
      0x43, 0x04,             // Ack Block Section (ACK Block 1 Length)
      0x85, 0x06, 0x07, 0x08, // Ack Block Section (Gap 2)
    };

    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::ACK);
    CHECK(frame1->valid() == false);
    frame1->~QUICFrame();
  }

  SECTION("0 Ack Block, 8 bit packet number length, 8 bit block length with ECN section")
  {
    uint8_t buf1[] = {
      0x03,       // Type
      0x12,       // Largest Acknowledged
      0x74, 0x56, // Ack Delay
      0x00,       // Ack Block Count
      0x00,       // Ack Block Section
      // ECN
      0x01, // ECT0
      0x02, // ECT1
      0x03, // ECN-CE
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::ACK);
    CHECK(frame1->size() == 9);
    const QUICAckFrame *ack_frame1 = static_cast<const QUICAckFrame *>(frame1);
    CHECK(ack_frame1 != nullptr);
    CHECK(ack_frame1->ack_block_count() == 0);
    CHECK(ack_frame1->largest_acknowledged() == 0x12);
    CHECK(ack_frame1->ack_delay() == 0x3456);
    CHECK(ack_frame1->ecn_section());
    CHECK(ack_frame1->ecn_section()->ect0_count() == 1);
    CHECK(ack_frame1->ecn_section()->ect1_count() == 2);
    CHECK(ack_frame1->ecn_section()->ecn_ce_count() == 3);
    frame1->~QUICFrame();
  }

  SECTION("0 Ack Block, 8 bit packet number length, 8 bit block length with ECN section")
  {
    uint8_t buf1[] = {
      0x03,       // Type
      0x12,       // Largest Acknowledged
      0x74, 0x56, // Ack Delay
      0x00,       // Ack Block Count
      0x00,       // Ack Block Section
      // ECN
      0x01, // ECT0
      0x02, // ECT1
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::ACK);
    CHECK(frame1->valid() == false);
    frame1->~QUICFrame();
  }
}

TEST_CASE("Store Ack Frame", "[quic]")
{
  SECTION("0 Ack Block, 8 bit packet number length, 8 bit block length")
  {
    uint8_t buf[32] = {0};
    size_t len      = 0;

    uint8_t expected[] = {
      0x02,       // Type
      0x12,       // Largest Acknowledged
      0x74, 0x56, // Ack Delay
      0x00,       // Ack Block Count
      0x00,       // Ack Block Section
    };

    QUICAckFrame ack_frame(0x12, 0x3456, 0);
    CHECK(ack_frame.size() == 6);

    Ptr<IOBufferBlock> ibb = ack_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == 6);
    CHECK(memcmp(buf, expected, len) == 0);
  }

  SECTION("2 Ack Block, 8 bit packet number length, 8 bit block length")
  {
    uint8_t buf[32] = {0};
    size_t len      = 0;

    uint8_t expected[] = {
      0x02,                                           // Type
      0x12,                                           // Largest Acknowledged
      0x74, 0x56,                                     // Ack Delay
      0x02,                                           // Ack Block Count
      0x01,                                           // Ack Block Section (First ACK Block Length)
      0x02,                                           // Ack Block Section (Gap 1)
      0x43, 0x04,                                     // Ack Block Section (ACK Block 1 Length)
      0x85, 0x06, 0x07, 0x08,                         // Ack Block Section (Gap 2)
      0xc9, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, // Ack Block Section (ACK Block 2 Length)
    };
    QUICAckFrame ack_frame(0x12, 0x3456, 0x01);
    QUICAckFrame::AckBlockSection *section = ack_frame.ack_block_section();
    section->add_ack_block({0x02, 0x0304});
    section->add_ack_block({0x05060708, 0x090a0b0c0d0e0f10});
    CHECK(ack_frame.size() == 21);

    Ptr<IOBufferBlock> ibb = ack_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == 21);
    CHECK(memcmp(buf, expected, len) == 0);
  }
}

TEST_CASE("Load RESET_STREAM Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("Load")
  {
    uint8_t buf1[] = {
      0x04,                                          // Type
      0x92, 0x34, 0x56, 0x78,                        // Stream ID
      0x01,                                          // Error Code
      0xd1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 // Final Offset
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::RESET_STREAM);
    CHECK(frame1->size() == 14);
    const QUICRstStreamFrame *rst_stream_frame1 = static_cast<const QUICRstStreamFrame *>(frame1);
    CHECK(rst_stream_frame1 != nullptr);
    CHECK(rst_stream_frame1->error_code() == 0x0001);
    CHECK(rst_stream_frame1->stream_id() == 0x12345678);
    CHECK(rst_stream_frame1->final_offset() == 0x1122334455667788);
  }

  SECTION("BAD Load")
  {
    uint8_t buf1[] = {
      0x04,                   // Type
      0x92, 0x34, 0x56, 0x78, // Stream ID
      0x01,                   // Error Code
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::RESET_STREAM);
    CHECK(frame1->valid() == false);
  }
}

TEST_CASE("Store RESET_STREAM Frame", "[quic]")
{
  uint8_t buf[65535];
  size_t len = 0;

  uint8_t expected[] = {
    0x04,                                          // Type
    0x92, 0x34, 0x56, 0x78,                        // Stream ID
    0x01,                                          // Error Code
    0xd1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 // Final Offset
  };
  QUICRstStreamFrame rst_stream_frame(0x12345678, 0x0001, 0x1122334455667788);
  CHECK(rst_stream_frame.size() == 14);

  Ptr<IOBufferBlock> ibb = rst_stream_frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 14);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load Ping Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  uint8_t buf[] = {
    0x01, // Type
  };
  const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
  CHECK(frame->type() == QUICFrameType::PING);
  CHECK(frame->size() == 1);

  const QUICPingFrame *ping_frame = static_cast<const QUICPingFrame *>(frame);
  CHECK(ping_frame != nullptr);
}

TEST_CASE("Store Ping Frame", "[quic]")
{
  uint8_t buf[16];
  size_t len = 0;

  uint8_t expected[] = {
    0x01, // Type
  };

  QUICPingFrame frame;
  CHECK(frame.size() == 1);

  Ptr<IOBufferBlock> ibb = frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 1);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load Padding Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  uint8_t buf1[] = {
    0x00, 0x00, 0x00 // Type
  };
  const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
  CHECK(frame1->type() == QUICFrameType::PADDING);
  CHECK(frame1->size() == 3);
  const QUICPaddingFrame *paddingFrame1 = static_cast<const QUICPaddingFrame *>(frame1);
  CHECK(paddingFrame1 != nullptr);
}

TEST_CASE("Store Padding Frame", "[quic]")
{
  uint8_t buf[65535];
  size_t len = 0;

  uint8_t expected[] = {
    0x00, 0x00, 0x00, // Type
  };
  QUICPaddingFrame padding_frame(3);
  Ptr<IOBufferBlock> ibb = padding_frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 3);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load HandshakeDone Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  uint8_t buf[] = {
    0x1e, // Type
  };
  const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
  CHECK(frame->type() == QUICFrameType::HANDSHAKE_DONE);
  CHECK(frame->size() == 1);

  const QUICHandshakeDoneFrame *handshake_done_frame = static_cast<const QUICHandshakeDoneFrame *>(frame);
  CHECK(handshake_done_frame != nullptr);
}

TEST_CASE("Store HandshakeDone Frame", "[quic]")
{
  uint8_t buf[16];
  size_t len = 0;

  uint8_t expected[] = {
    0x1e, // Type
  };

  QUICHandshakeDoneFrame frame;
  CHECK(frame.size() == 1);

  Ptr<IOBufferBlock> ibb = frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 1);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("ConnectionClose Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  uint8_t reason_phrase[]  = {0x41, 0x42, 0x43, 0x44, 0x45};
  size_t reason_phrase_len = sizeof(reason_phrase);

  SECTION("loading w/ reason phrase")
  {
    uint8_t buf[] = {
      0x1c,                        // Type
      0x0A,                        // Error Code
      0x00,                        // Frame Type
      0x05,                        // Reason Phrase Length
      0x41, 0x42, 0x43, 0x44, 0x45 // Reason Phrase ("ABCDE");
    };

    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::CONNECTION_CLOSE);
    CHECK(frame->size() == sizeof(buf));

    const QUICConnectionCloseFrame *conn_close_frame = static_cast<const QUICConnectionCloseFrame *>(frame);
    CHECK(conn_close_frame != nullptr);
    CHECK(conn_close_frame->error_code() == static_cast<uint16_t>(QUICTransErrorCode::PROTOCOL_VIOLATION));
    CHECK(conn_close_frame->frame_type() == QUICFrameType::UNKNOWN);
    CHECK(conn_close_frame->reason_phrase_length() == reason_phrase_len);
    CHECK(memcmp(conn_close_frame->reason_phrase(), reason_phrase, reason_phrase_len) == 0);
  }

  SECTION("Bad loading")
  {
    uint8_t buf[] = {
      0x1c, // Type
      0x0A, // Error Code
      0x00, // Frame Type
      0x05, // Reason Phrase Length
    };

    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::CONNECTION_CLOSE);
    CHECK(frame->valid() == false);
  }

  SECTION("loading w/o reason phrase")
  {
    uint8_t buf[] = {
      0x1c, // Type
      0x0A, // Error Code
      0x04, // Frame Type
      0x00, // Reason Phrase Length
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::CONNECTION_CLOSE);
    CHECK(frame->size() == sizeof(buf));

    const QUICConnectionCloseFrame *conn_close_frame = static_cast<const QUICConnectionCloseFrame *>(frame);
    CHECK(conn_close_frame != nullptr);
    CHECK(conn_close_frame->error_code() == static_cast<uint16_t>(QUICTransErrorCode::PROTOCOL_VIOLATION));
    CHECK(conn_close_frame->frame_type() == QUICFrameType::RESET_STREAM);
    CHECK(conn_close_frame->reason_phrase_length() == 0);
  }

  SECTION("storing w/ reason phrase")
  {
    uint8_t buf[32];
    size_t len = 0;

    uint8_t expected[] = {
      0x1c,                        // Type
      0x0A,                        // Error Code
      0x08,                        // Frame Type
      0x05,                        // Reason Phrase Length
      0x41, 0x42, 0x43, 0x44, 0x45 // Reason Phrase ("ABCDE");
    };
    QUICConnectionCloseFrame connection_close_frame(static_cast<uint16_t>(QUICTransErrorCode::PROTOCOL_VIOLATION),
                                                    QUICFrameType::STREAM, 5, "ABCDE");
    CHECK(connection_close_frame.size() == sizeof(expected));

    Ptr<IOBufferBlock> ibb = connection_close_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == sizeof(expected));
    CHECK(memcmp(buf, expected, len) == 0);
  }

  SECTION("storing w/o reason phrase")
  {
    uint8_t buf[32];
    size_t len = 0;

    uint8_t expected[] = {
      0x1c, // Type
      0x0A, // Error Code
      0x00, // Frame Type
      0x00, // Reason Phrase Length
    };
    QUICConnectionCloseFrame connection_close_frame(static_cast<uint16_t>(QUICTransErrorCode::PROTOCOL_VIOLATION),
                                                    QUICFrameType::UNKNOWN, 0, nullptr);
    Ptr<IOBufferBlock> ibb = connection_close_frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == sizeof(expected));
    CHECK(memcmp(buf, expected, len) == 0);
  }
}

TEST_CASE("Load MaxData Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("Load")
  {
    uint8_t buf1[] = {
      0x10,                                          // Type
      0xd1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 // Maximum Data
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::MAX_DATA);
    CHECK(frame1->size() == 9);
    const QUICMaxDataFrame *max_data_frame = static_cast<const QUICMaxDataFrame *>(frame1);
    CHECK(max_data_frame != nullptr);
    CHECK(max_data_frame->maximum_data() == 0x1122334455667788ULL);
  }

  SECTION("Bad Load")
  {
    uint8_t buf1[] = {
      0x10, // Type
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::MAX_DATA);
    CHECK(frame1->valid() == false);
  }
}

TEST_CASE("Store MaxData Frame", "[quic]")
{
  uint8_t buf[65535];
  size_t len = 0;

  uint8_t expected[] = {
    0x10,                                          // Type
    0xd1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 // Maximum Data
  };
  QUICMaxDataFrame max_data_frame(0x1122334455667788, 0, nullptr);
  CHECK(max_data_frame.size() == 9);

  Ptr<IOBufferBlock> ibb = max_data_frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 9);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load MaxStreamData Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("Load")
  {
    uint8_t buf1[] = {
      0x11,                                          // Type
      0x81, 0x02, 0x03, 0x04,                        // Stream ID
      0xd1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 // Maximum Stream Data
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::MAX_STREAM_DATA);
    CHECK(frame1->size() == 13);
    const QUICMaxStreamDataFrame *maxStreamDataFrame1 = static_cast<const QUICMaxStreamDataFrame *>(frame1);
    CHECK(maxStreamDataFrame1 != nullptr);
    CHECK(maxStreamDataFrame1->stream_id() == 0x01020304);
    CHECK(maxStreamDataFrame1->maximum_stream_data() == 0x1122334455667788ULL);
  }

  SECTION("Load")
  {
    uint8_t buf1[] = {
      0x11,                   // Type
      0x81, 0x02, 0x03, 0x04, // Stream ID
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::MAX_STREAM_DATA);
    CHECK(frame1->valid() == false);
  }
}

TEST_CASE("Store MaxStreamData Frame", "[quic]")
{
  uint8_t buf[65535];
  size_t len = 0;

  uint8_t expected[] = {
    0x11,                                          // Type
    0x81, 0x02, 0x03, 0x04,                        // Stream ID
    0xd1, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 // Maximum Stream Data
  };
  QUICMaxStreamDataFrame max_stream_data_frame(0x01020304, 0x1122334455667788ULL);
  CHECK(max_stream_data_frame.size() == 13);

  Ptr<IOBufferBlock> ibb = max_stream_data_frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 13);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load MaxStreams Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("load")
  {
    uint8_t buf1[] = {
      0x12,                   // Type
      0x81, 0x02, 0x03, 0x04, // Stream ID
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::MAX_STREAMS);
    CHECK(frame1->size() == 5);
    const QUICMaxStreamsFrame *max_streams_frame = static_cast<const QUICMaxStreamsFrame *>(frame1);
    CHECK(max_streams_frame != nullptr);
    CHECK(max_streams_frame->maximum_streams() == 0x01020304);
  }
  SECTION("bad load")
  {
    uint8_t buf1[] = {
      0x12, // Type
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::MAX_STREAMS);
    CHECK(frame1->valid() == false);
  }
}

TEST_CASE("Store MaxStreams Frame", "[quic]")
{
  uint8_t buf[65535];
  size_t len = 0;

  uint8_t expected[] = {
    0x12,                   // Type
    0x81, 0x02, 0x03, 0x04, // Stream ID
  };
  QUICMaxStreamsFrame max_streams_frame(0x01020304, 0, nullptr);
  CHECK(max_streams_frame.size() == 5);

  Ptr<IOBufferBlock> ibb = max_streams_frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 5);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load DataBlocked Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("load")
  {
    uint8_t buf1[] = {
      0x14, // Type
      0x07, // Offset
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::DATA_BLOCKED);
    CHECK(frame1->size() == 2);
    const QUICDataBlockedFrame *blocked_stream_frame = static_cast<const QUICDataBlockedFrame *>(frame1);
    CHECK(blocked_stream_frame != nullptr);
    CHECK(blocked_stream_frame->offset() == 0x07);
  }

  SECTION("bad load")
  {
    uint8_t buf1[] = {
      0x14, // Type
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::DATA_BLOCKED);
    CHECK(frame1->valid() == false);
  }
}

TEST_CASE("Store DataBlocked Frame", "[quic]")
{
  uint8_t buf[65535];
  size_t len = 0;

  uint8_t expected[] = {
    0x14, // Type
    0x07, // Offset
  };
  QUICDataBlockedFrame blocked_stream_frame(0x07, 0, nullptr);
  CHECK(blocked_stream_frame.size() == 2);

  Ptr<IOBufferBlock> ibb = blocked_stream_frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 2);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load StreamDataBlocked Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("Load")
  {
    uint8_t buf1[] = {
      0x15,                   // Type
      0x81, 0x02, 0x03, 0x04, // Stream ID
      0x07,                   // Offset
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::STREAM_DATA_BLOCKED);
    CHECK(frame1->size() == 6);
    const QUICStreamDataBlockedFrame *stream_blocked_frame = static_cast<const QUICStreamDataBlockedFrame *>(frame1);
    CHECK(stream_blocked_frame != nullptr);
    CHECK(stream_blocked_frame->stream_id() == 0x01020304);
    CHECK(stream_blocked_frame->offset() == 0x07);
  }

  SECTION("Load")
  {
    uint8_t buf1[] = {
      0x15,                   // Type
      0x81, 0x02, 0x03, 0x04, // Stream ID
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::STREAM_DATA_BLOCKED);
    CHECK(frame1->valid() == false);
  }
}

TEST_CASE("Store StreamDataBlocked Frame", "[quic]")
{
  uint8_t buf[65535];
  size_t len = 0;

  uint8_t expected[] = {
    0x15,                   // Type
    0x81, 0x02, 0x03, 0x04, // Stream ID
    0x07,                   // Offset
  };
  QUICStreamDataBlockedFrame stream_blocked_frame(0x01020304, 0x07);
  CHECK(stream_blocked_frame.size() == 6);

  Ptr<IOBufferBlock> ibb = stream_blocked_frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 6);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load StreamsBlocked Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("Load")
  {
    uint8_t buf1[] = {
      0x16,       // Type
      0x41, 0x02, // Stream ID
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::STREAMS_BLOCKED);
    CHECK(frame1->size() == 3);
    const QUICStreamIdBlockedFrame *stream_id_blocked_frame = static_cast<const QUICStreamIdBlockedFrame *>(frame1);
    CHECK(stream_id_blocked_frame != nullptr);
    CHECK(stream_id_blocked_frame->stream_id() == 0x0102);
  }

  SECTION("Load")
  {
    uint8_t buf1[] = {
      0x16, // Type
    };
    const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
    CHECK(frame1->type() == QUICFrameType::STREAMS_BLOCKED);
    CHECK(frame1->valid() == false);
  }
}

TEST_CASE("Store StreamsBlocked Frame", "[quic]")
{
  uint8_t buf[65535];
  size_t len = 0;

  uint8_t expected[] = {
    0x16,       // Type
    0x41, 0x02, // Stream ID
  };
  QUICStreamIdBlockedFrame stream_id_blocked_frame(0x0102, 0, nullptr);
  CHECK(stream_id_blocked_frame.size() == 3);

  Ptr<IOBufferBlock> ibb = stream_id_blocked_frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 3);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load NewConnectionId Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];

  SECTION("load")
  {
    uint8_t buf[] = {
      0x18,                                           // Type
      0x41, 0x02,                                     // Sequence
      0x41, 0x00,                                     // Retire Prior To
      0x08,                                           // Length
      0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, // Connection ID
      0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, // Stateless Reset Token
      0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::NEW_CONNECTION_ID);
    CHECK(frame->size() == sizeof(buf));
    CHECK(frame->valid() == true);

    const QUICNewConnectionIdFrame *new_con_id_frame = static_cast<const QUICNewConnectionIdFrame *>(frame);
    CHECK(new_con_id_frame != nullptr);
    CHECK(new_con_id_frame->sequence() == 0x0102);
    CHECK(new_con_id_frame->retire_prior_to() == 0x0100);
    CHECK((new_con_id_frame->connection_id() ==
           QUICConnectionId(reinterpret_cast<const uint8_t *>("\x11\x22\x33\x44\x55\x66\x77\x88"), 8)));
    CHECK(memcmp(new_con_id_frame->stateless_reset_token().buf(), buf + sizeof(buf) - QUICStatelessResetToken::LEN,
                 QUICStatelessResetToken::LEN) == 0);
  }

  SECTION("Bad Load")
  {
    uint8_t buf[] = {
      0x18,                                           // Type
      0x41, 0x02,                                     // Sequence
      0x41, 0x00,                                     // Retire Prior To
      0x08,                                           // Length
      0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, // Connection ID
      0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, // Stateless Reset Token
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::NEW_CONNECTION_ID);
    CHECK(frame->valid() == false);
  }
}

TEST_CASE("Store NewConnectionId Frame", "[quic]")
{
  uint8_t buf[32];
  size_t len = 0;

  uint8_t expected[] = {
    0x18,                                           // Type
    0x41, 0x02,                                     // Sequence Number
    0x41, 0x00,                                     // Retire Prior To
    0x08,                                           // Length
    0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, // Connection ID
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // Stateless Reset Token
    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
  };
  QUICNewConnectionIdFrame new_con_id_frame(0x0102, 0x0100,
                                            {reinterpret_cast<const uint8_t *>("\x11\x22\x33\x44\x55\x66\x77\x88"), 8},
                                            {expected + sizeof(expected) - QUICStatelessResetToken::LEN});
  CHECK(new_con_id_frame.size() == sizeof(expected));

  Ptr<IOBufferBlock> ibb = new_con_id_frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == sizeof(expected));
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load STOP_SENDING Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("LOAD")
  {
    uint8_t buf[] = {
      0x05,                   // Type
      0x92, 0x34, 0x56, 0x78, // Stream ID
      0x01,                   // Error Code
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::STOP_SENDING);
    CHECK(frame->size() == 6);

    const QUICStopSendingFrame *stop_sending_frame = static_cast<const QUICStopSendingFrame *>(frame);
    CHECK(stop_sending_frame != nullptr);
    CHECK(stop_sending_frame->stream_id() == 0x12345678);
    CHECK(stop_sending_frame->error_code() == 0x01);
  }

  SECTION("Bad LOAD")
  {
    uint8_t buf[] = {
      0x05,                   // Type
      0x92, 0x34, 0x56, 0x78, // Stream ID
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::STOP_SENDING);
    CHECK(frame->valid() == false);
  }
}

TEST_CASE("Store STOP_SENDING Frame", "[quic]")
{
  uint8_t buf[65535];
  size_t len = 0;

  uint8_t expected[] = {
    0x05,                   // Type
    0x92, 0x34, 0x56, 0x78, // Stream ID
    0x01,                   // Error Code
  };
  QUICStopSendingFrame stop_sending_frame(0x12345678, static_cast<QUICAppErrorCode>(0x01));
  CHECK(stop_sending_frame.size() == 6);

  Ptr<IOBufferBlock> ibb = stop_sending_frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 6);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load PATH_CHALLENGE Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("Load")
  {
    uint8_t buf[] = {
      0x1a,                                           // Type
      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, // Data
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::PATH_CHALLENGE);
    CHECK(frame->size() == 9);

    const QUICPathChallengeFrame *path_challenge_frame = static_cast<const QUICPathChallengeFrame *>(frame);
    CHECK(path_challenge_frame != nullptr);
    CHECK(memcmp(path_challenge_frame->data(), "\x01\x23\x45\x67\x89\xab\xcd\xef", QUICPathChallengeFrame::DATA_LEN) == 0);
    frame->~QUICFrame();
  }

  SECTION("Load")
  {
    uint8_t buf[] = {
      0x1a,                                     // Type
      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xef, // Data
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::PATH_CHALLENGE);
    CHECK(frame->valid() == false);
    frame->~QUICFrame();
  }
}

TEST_CASE("Store PATH_CHALLENGE Frame", "[quic]")
{
  uint8_t buf[16];
  size_t len = 0;

  uint8_t expected[] = {
    0x1a,                                           // Type
    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, // Data
  };

  uint8_t raw[]       = "\x01\x23\x45\x67\x89\xab\xcd\xef";
  size_t raw_len      = sizeof(raw) - 1;
  ats_unique_buf data = ats_unique_malloc(raw_len);
  memcpy(data.get(), raw, raw_len);

  QUICPathChallengeFrame frame(std::move(data));
  CHECK(frame.size() == 9);

  Ptr<IOBufferBlock> ibb = frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 9);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("Load PATH_RESPONSE Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  SECTION("Load")
  {
    uint8_t buf[] = {
      0x1b,                                           // Type
      0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, // Data
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::PATH_RESPONSE);
    CHECK(frame->size() == 9);

    const QUICPathResponseFrame *path_response_frame = static_cast<const QUICPathResponseFrame *>(frame);
    CHECK(path_response_frame != nullptr);
    CHECK(memcmp(path_response_frame->data(), "\x01\x23\x45\x67\x89\xab\xcd\xef", QUICPathResponseFrame::DATA_LEN) == 0);
    frame->~QUICFrame();
  }

  SECTION("Load")
  {
    uint8_t buf[] = {
      0x1b,                                     // Type
      0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, // Data
    };
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, buf, sizeof(buf), nullptr);
    CHECK(frame->type() == QUICFrameType::PATH_RESPONSE);
    CHECK(frame->valid() == false);
    frame->~QUICFrame();
  }
}

TEST_CASE("Store PATH_RESPONSE Frame", "[quic]")
{
  uint8_t buf[16];
  size_t len = 0;

  uint8_t expected[] = {
    0x1b,                                           // Type
    0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, // Data
  };

  uint8_t raw[]       = "\x01\x23\x45\x67\x89\xab\xcd\xef";
  size_t raw_len      = sizeof(raw) - 1;
  ats_unique_buf data = ats_unique_malloc(raw_len);
  memcpy(data.get(), raw, raw_len);

  QUICPathResponseFrame frame(std::move(data));
  CHECK(frame.size() == 9);

  Ptr<IOBufferBlock> ibb = frame.to_io_buffer_block(sizeof(buf));
  for (auto b = ibb; b; b = b->next) {
    memcpy(buf + len, b->start(), b->size());
    len += b->size();
  }
  CHECK(len == 9);
  CHECK(memcmp(buf, expected, len) == 0);
}

TEST_CASE("NEW_TOKEN Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  uint8_t raw_new_token_frame[] = {
    0x07,                                           // Type
    0x08,                                           // Token Length (i)
    0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, // Token (*)
  };
  size_t raw_new_token_frame_len = sizeof(raw_new_token_frame);

  uint8_t raw_token[]  = {0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef, 0xbe, 0xef};
  size_t raw_token_len = sizeof(raw_token);

  SECTION("load")
  {
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, raw_new_token_frame, raw_new_token_frame_len, nullptr);
    CHECK(frame->type() == QUICFrameType::NEW_TOKEN);
    CHECK(frame->size() == raw_new_token_frame_len);

    const QUICNewTokenFrame *new_token_frame = static_cast<const QUICNewTokenFrame *>(frame);
    CHECK(new_token_frame != nullptr);
    CHECK(new_token_frame->token_length() == raw_token_len);
    CHECK(memcmp(new_token_frame->token(), raw_token, raw_token_len) == 0);
    frame->~QUICFrame();
  }

  SECTION("bad load")
  {
    const QUICFrame *frame = QUICFrameFactory::create(frame_buf, raw_new_token_frame, raw_new_token_frame_len - 5, nullptr);
    CHECK(frame->type() == QUICFrameType::NEW_TOKEN);
    CHECK(frame->valid() == false);
    frame->~QUICFrame();
  }

  SECTION("store")
  {
    uint8_t buf[32];
    size_t len = 0;

    ats_unique_buf token = ats_unique_malloc(raw_token_len);
    memcpy(token.get(), raw_token, raw_token_len);

    QUICNewTokenFrame frame(std::move(token), raw_token_len);
    CHECK(frame.size() == raw_new_token_frame_len);

    Ptr<IOBufferBlock> ibb = frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == raw_new_token_frame_len);
    CHECK(memcmp(buf, raw_new_token_frame, len) == 0);
  }
}

TEST_CASE("RETIRE_CONNECTION_ID Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  uint8_t raw_retire_connection_id_frame[] = {
    0x19, // Type
    0x08, // Sequence Number (i)
  };
  size_t raw_retire_connection_id_frame_len = sizeof(raw_retire_connection_id_frame);
  uint64_t seq_num                          = 8;

  SECTION("load")
  {
    const QUICFrame *frame =
      QUICFrameFactory::create(frame_buf, raw_retire_connection_id_frame, raw_retire_connection_id_frame_len, nullptr);
    CHECK(frame->type() == QUICFrameType::RETIRE_CONNECTION_ID);
    CHECK(frame->size() == raw_retire_connection_id_frame_len);

    const QUICRetireConnectionIdFrame *retire_connection_id_frame = static_cast<const QUICRetireConnectionIdFrame *>(frame);
    CHECK(retire_connection_id_frame != nullptr);
    CHECK(retire_connection_id_frame->seq_num() == seq_num);
  }

  SECTION("bad load")
  {
    const QUICFrame *frame =
      QUICFrameFactory::create(frame_buf, raw_retire_connection_id_frame, raw_retire_connection_id_frame_len - 1, nullptr);
    CHECK(frame->type() == QUICFrameType::RETIRE_CONNECTION_ID);
    CHECK(frame->valid() == false);
  }

  SECTION("store")
  {
    uint8_t buf[32];
    size_t len = 0;

    QUICRetireConnectionIdFrame frame(seq_num, 0, nullptr);
    CHECK(frame.size() == raw_retire_connection_id_frame_len);

    Ptr<IOBufferBlock> ibb = frame.to_io_buffer_block(sizeof(buf));
    for (auto b = ibb; b; b = b->next) {
      memcpy(buf + len, b->start(), b->size());
      len += b->size();
    }
    CHECK(len == raw_retire_connection_id_frame_len);
    CHECK(memcmp(buf, raw_retire_connection_id_frame, len) == 0);
  }
}

TEST_CASE("QUICFrameFactory Create Unknown Frame", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  uint8_t buf1[] = {
    0x20, // Type
  };
  const QUICFrame *frame1 = QUICFrameFactory::create(frame_buf, buf1, sizeof(buf1), nullptr);
  CHECK(frame1 == nullptr);
}

TEST_CASE("QUICFrameFactory Fast Create Frame", "[quic]")
{
  QUICFrameFactory factory;

  uint8_t buf1[] = {
    0x12,                   // Type
    0x81, 0x02, 0x03, 0x04, // Stream Data
  };
  uint8_t buf2[] = {
    0x12,                   // Type
    0x85, 0x06, 0x07, 0x08, // Stream Data
  };
  const QUICFrame &frame1 = factory.fast_create(buf1, sizeof(buf1), nullptr);
  CHECK(frame1.type() == QUICFrameType::MAX_STREAMS);

  const QUICMaxStreamsFrame &max_streams_frame1 = static_cast<const QUICMaxStreamsFrame &>(frame1);
  CHECK(max_streams_frame1.maximum_streams() == 0x01020304);

  const QUICFrame &frame2 = factory.fast_create(buf2, sizeof(buf2), nullptr);
  CHECK(frame2.type() == QUICFrameType::MAX_STREAMS);

  const QUICMaxStreamsFrame &max_streams_frame2 = static_cast<const QUICMaxStreamsFrame &>(frame2);
  CHECK(max_streams_frame2.maximum_streams() == 0x05060708);

  CHECK(&frame1 == &frame2);
}

TEST_CASE("QUICFrameFactory Fast Create Unknown Frame", "[quic]")
{
  QUICFrameFactory factory;

  uint8_t buf1[] = {
    0x20, // Type
  };
  const QUICFrame &frame1 = factory.fast_create(buf1, sizeof(buf1), nullptr);
  CHECK(frame1.type() == QUICFrameType::UNKNOWN);
}

TEST_CASE("QUICFrameFactory Create CONNECTION_CLOSE with a QUICConnectionError", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];

  std::unique_ptr<QUICConnectionError> error =
    std::unique_ptr<QUICConnectionError>(new QUICConnectionError(QUICTransErrorCode::INTERNAL_ERROR));
  const QUICConnectionCloseFrame *connection_close_frame1 = QUICFrameFactory::create_connection_close_frame(frame_buf, *error);
  CHECK(connection_close_frame1->error_code() == static_cast<uint16_t>(QUICTransErrorCode::INTERNAL_ERROR));
  CHECK(connection_close_frame1->reason_phrase_length() == 0);
  CHECK(connection_close_frame1->reason_phrase() == nullptr);

  error = std::unique_ptr<QUICConnectionError>(new QUICConnectionError(QUICTransErrorCode::INTERNAL_ERROR, "test"));
  const QUICConnectionCloseFrame *connection_close_frame2 = QUICFrameFactory::create_connection_close_frame(frame_buf, *error);
  CHECK(connection_close_frame2->error_code() == static_cast<uint16_t>(QUICTransErrorCode::INTERNAL_ERROR));
  CHECK(connection_close_frame2->reason_phrase_length() == 4);
  CHECK(memcmp(connection_close_frame2->reason_phrase(), "test", 4) == 0);
}

TEST_CASE("QUICFrameFactory Create RESET_STREAM with a QUICStreamError", "[quic]")
{
  uint8_t frame_buf[QUICFrame::MAX_INSTANCE_SIZE];
  QUICRTTMeasure mock_rtt;
  MockQUICConnection mock_connection;
  QUICBidirectionalStream stream(&mock_rtt, &mock_connection, 0x1234, 0, 0);
  std::unique_ptr<QUICStreamError> error =
    std::unique_ptr<QUICStreamError>(new QUICStreamError(&stream, static_cast<QUICAppErrorCode>(0x01)));
  const QUICRstStreamFrame *rst_stream_frame1 = QUICFrameFactory::create_rst_stream_frame(frame_buf, *error);
  CHECK(rst_stream_frame1->error_code() == 0x01);
  CHECK(rst_stream_frame1->stream_id() == 0x1234);
  CHECK(rst_stream_frame1->final_offset() == 0);
}
