// Copyright 2021 The libgav1 Authors
//
// Licensed 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 "src/obu_parser.h"

#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <new>
#include <string>
#include <vector>

#include "gtest/gtest.h"
#include "src/buffer_pool.h"
#include "src/decoder_state.h"
#include "src/gav1/decoder_buffer.h"
#include "src/gav1/status_code.h"
#include "src/utils/common.h"
#include "src/utils/constants.h"
#include "src/utils/dynamic_buffer.h"
#include "src/utils/segmentation.h"
#include "src/utils/types.h"
#include "src/utils/vector.h"
#include "tests/third_party/libvpx/acm_random.h"

// Note the following test classes access private functions/members of
// ObuParser. To be declared friends of ObuParser they must not have internal
// linkage (they must be outside the anonymous namespace).
namespace libgav1 {

// Helper class to manipulate individual bits and generate a byte string.
class BytesAndBits {
 public:
  // Append a bit to the end.
  void AppendBit(uint8_t bit) { bits_.push_back(bit != 0); }

  // Append a byte to the end.
  void AppendByte(uint8_t byte) {
    for (int i = 0; i < 8; ++i) {
      AppendBit(GetNthBit(byte, i, 8));
    }
  }

  // Append a literal of size |bits| to the end.
  void AppendLiteral(int bits, int value) {
    InsertLiteral(static_cast<int>(bits_.size()), bits, value);
  }

  // Append an inverse signed literal to the end. |bits + 1| bits are appended.
  void AppendInverseSignedLiteral(int bits, int value) {
    InsertInverseSignedLiteral(static_cast<int>(bits_.size()), bits, value);
  }

  // Append a sequence of bytes to the end.
  void AppendBytes(const std::vector<uint8_t>& bytes) {
    for (const auto& byte : bytes) {
      AppendByte(byte);
    }
  }

  // Insert |bit| in |offset|. Moves all other bits to the right by 1.
  void InsertBit(int offset, uint8_t bit) {
    auto iterator = bits_.begin();
    bits_.insert(iterator + offset, bit != 0);
  }

  // Insert |value| of size |bits| at offset |offset|. Moves all other bits to
  // the right by |bits|.
  void InsertLiteral(int offset, int bits, int value) {
    for (int i = 0; i < bits; ++i) {
      InsertBit(i + offset, GetNthBit(value, i, bits));
    }
  }

  // Insert |value| of size |bits| at offset |offset| as an inverse signed
  // literal. Move all other bits to the right by |bits + 1|.
  //
  // Note: This is denoted su(1+bits) in the spec.
  void InsertInverseSignedLiteral(int offset, int bits, int value) {
    InsertBit(offset, (value >= 0) ? 0 : 1);
    InsertLiteral(offset + 1, bits, value);
  }

  // Insert |value| at |offset| as an unsigned variable length code (uvlc).
  // Return the number of bits inserted.
  int InsertUvlc(int offset, int value) {
    int leading_zeros = 1;
    int shift_value = ++value;
    while ((shift_value >>= 1) != 0) leading_zeros += 2;
    int bits = 0;
    InsertLiteral(offset, leading_zeros >> 1, 0);
    bits += leading_zeros >> 1;
    InsertLiteral(offset + bits, (leading_zeros + 1) >> 1, value);
    bits += (leading_zeros + 1) >> 1;
    return bits;
  }

  // Set the bit at |offset| to |bit|. The bit should already exist.
  void SetBit(int offset, uint8_t bit) { bits_[offset] = bit != 0; }

  // Set |bits| starting at |offset| to |value|. The bits should already exist.
  void SetLiteral(int offset, int bits, int value) {
    for (int i = 0; i < bits; ++i) {
      SetBit(offset + i, GetNthBit(value, i, bits));
    }
  }

  // Remove a bit in |offset|. Moves over all the following bits to the left by
  // 1.
  void RemoveBit(int offset) { RemoveLiteral(offset, 1); }

  // Remove a literal of size |bits| from |offset|. Moves over all the
  // following bits to the left by |bits|.
  void RemoveLiteral(int offset, int bits) {
    bits_.erase(bits_.begin() + offset, bits_.begin() + offset + bits);
  }

  // Remove all bits after offset.
  void RemoveAllBitsAfter(int offset) {
    RemoveLiteral(offset, static_cast<int>(bits_.size()) - offset);
  }

  // Clear all the bits stored.
  void Clear() { bits_.clear(); }

  // Generate the data vector from the bits. Pads 0 to the end of the last byte
  // if necessary.
  const std::vector<uint8_t>& GenerateData() {
    data_.clear();
    for (size_t i = 0; i < bits_.size(); i += 8) {
      uint8_t byte = 0;
      for (int j = 0; j < 8; ++j) {
        const uint8_t bit =
            ((i + j) < bits_.size()) ? static_cast<uint8_t>(bits_[i + j]) : 0;
        byte |= bit << (7 - j);
      }
      data_.push_back(byte);
    }
    return data_;
  }

 private:
  // Get the |n|th MSB from |value| with the assumption that |value| has |size|
  // bits.
  static uint8_t GetNthBit(int value, int n, int size) {
    return (value >> (size - n - 1)) & 0x01;
  }

  std::vector<uint8_t> data_;
  std::vector<bool> bits_;
};

class ObuParserTest : public testing::Test {
 protected:
  // Constants for unit tests.
  static constexpr int kFrameWidthBits = 9;
  static constexpr int kFrameHeightBits = 8;
  static constexpr int kHeight = 240;
  static constexpr int kWidth = 426;
  static constexpr int kRows4x4 = 60;
  static constexpr int kColumns4x4 = 108;
  static constexpr int kFrameToShow = 2;
  static constexpr int kDisplayFrameId = 10;
  static constexpr int kFrameIdLengthBits = 15;
  static constexpr int kDeltaFrameIdLengthBits = 14;

  // Bit streams for testing. These may contain trailing bits and tests may have
  // to remove some of the trailing bits to keep the boundary alignment.
  const std::vector<uint8_t> kDefaultTemporalDelimiter = {0x12, 0x00};
  // Bits  Syntax element                  Value
  // 1     obu_forbidden_bit               0
  // 4     obu_type                        2 (OBU_TEMPORAL_DELIMITER)
  // 1     obu_extension_flag              1
  // 1     obu_has_size_field              1
  // 1     obu_reserved_1bit               0
  // 3     temporal_id                     6
  // 2     spatial_id                      2
  // 3     extension_header_reserved_3bits 0
  // 8     obu_size                        0
  const std::vector<uint8_t> kDefaultTemporalDelimiterWithExtension = {
      0x16, 0xd0, 0x00};
  const std::vector<uint8_t> kDefaultHeaderWithoutSizeField = {0x10};
  // Offset  Bits  Syntax element                     Value
  // 0       3     seq_profile                        0
  // 3       1     still_picture                      0
  // 4       1     reduced_still_picture_header       0
  // 5       1     timing_info_present_flag           0
  // 6       1     initial_display_delay_present_flag 0
  // 7       5     operating_points_cnt_minus_1       0
  // 12      12    operating_point_idc[ 0 ]           0
  // 24      5     seq_level_idx[ 0 ]                 0
  // 29      4     frame_width_bits_minus_1           8
  // 33      4     frame_height_bits_minus_1          7
  // 37      9     max_frame_width_minus_1            425
  // 46      8     max_frame_height_minus_1           239
  // 54      1     frame_id_numbers_present_flag      0
  // 55      1     use_128x128_superblock             1
  // 56      1     enable_filter_intra                1
  // 57      1     enable_intra_edge_filter           1
  // 58      1     enable_interintra_compound         1
  // 59      1     enable_masked_compound             1
  // 60      1     enable_warped_motion               0
  // 61      1     enable_dual_filter                 1
  // 62      1     enable_order_hint                  1
  // 63      1     enable_jnt_comp                    1
  // 64      1     enable_ref_frame_mvs               1
  // 65      1     seq_choose_screen_content_tools    1
  // 66      1     seq_choose_integer_mv              1
  // 67      3     order_hint_bits_minus_1            6
  // 70      1     enable_superres                    0
  // 71      1     enable_cdef                        1
  // 72      1     enable_restoration                 1
  // ...
  const std::vector<uint8_t> kDefaultSequenceHeader = {
      0x00, 0x00, 0x00, 0x04, 0x3e, 0xa7, 0xbd, 0xf7, 0xf9, 0x80, 0x40};
  const std::vector<uint8_t> kDefaultFrameHeaderKeyFrame = {0x10, 0x00};
  // Bits  Syntax element           Value
  // 1     show_existing_frame      0
  // 2     frame_type               2 (kFrameIntraOnly)
  // 1     show_frame               1
  // 1     error_resilient_mode     0
  // 1     disable_cdf_update       0
  // 1     frame_size_override_flag 0
  // 8     refresh_frame_flags      4
  // ...
  const std::vector<uint8_t> kDefaultFrameHeaderIntraOnlyFrame = {0x50, 0x08,
                                                                  0x00};
  // Bits  Syntax element           Value
  // 1     show_existing_frame      0
  // 2     frame_type               1 (kFrameInter)
  // 1     show_frame               1
  // 1     error_resilient_mode     0
  // 1     disable_cdf_update       0
  // 1     frame_size_override_flag 0
  // 3     primary_ref_frame        1
  // 8     refresh_frame_flags      4
  // 3     ref_frame_idx[0]         0
  // 3     ref_frame_idx[1]         1
  // 3     ref_frame_idx[2]         2
  // 3     ref_frame_idx[3]         3
  // 3     ref_frame_idx[4]         4
  // 3     ref_frame_idx[5]         5
  // 3     ref_frame_idx[6]         6
  // ...
  const std::vector<uint8_t> kDefaultFrameHeaderInterFrame = {0x30, 0x41, 0x01,
                                                              0x4e, 0x5c, 0x60};
  const std::vector<uint8_t> kDefaultGlobalMotionParametersRotZoom = {
      0xff, 0x50, 0x77, 0x7e, 0x1f, 0xcd};
  const std::vector<uint8_t> kDefaultGlobalMotionParametersAffine = {
      0x3f, 0x50, 0x77, 0x7b, 0xbf, 0xa8, 0x3e, 0x1f, 0xcd};

  void SetUp() override {
    buffer_pool_.reset(new (std::nothrow)
                           BufferPool(nullptr, nullptr, nullptr, nullptr));
    ASSERT_NE(buffer_pool_, nullptr);
  }

  bool Init() {
    obu_.reset(new (std::nothrow) ObuParser(nullptr, 0, 0, buffer_pool_.get(),
                                            &decoder_state_));
    if (obu_ == nullptr) return false;
    obu_headers_ = &obu_->obu_headers_;
    obu_frame_header_ = &obu_->frame_header_;
    obu_sequence_header_ = &obu_->sequence_header_;
    return true;
  }

  bool Init(const std::vector<uint8_t>& data, bool init_bit_reader = true) {
    obu_.reset(new (std::nothrow) ObuParser(
        data.data(), data.size(), 0, buffer_pool_.get(), &decoder_state_));
    if (obu_ == nullptr) return false;
    obu_headers_ = &obu_->obu_headers_;
    obu_frame_header_ = &obu_->frame_header_;
    obu_sequence_header_ = &obu_->sequence_header_;
    return init_bit_reader ? obu_->InitBitReader(data.data(), data.size())
                           : true;
  }

  bool Parse(const std::string& input,
             const ObuSequenceHeader* const sequence_header = nullptr) {
    std::vector<uint8_t> data(input.begin(), input.end());
    return Parse(data, sequence_header);
  }

  bool Parse(const std::vector<uint8_t>& data,
             const ObuSequenceHeader* const sequence_header = nullptr) {
    EXPECT_TRUE(Init(data, false));
    if (sequence_header != nullptr) obu_->set_sequence_header(*sequence_header);
    return obu_->ParseOneFrame(&current_frame_) == kStatusOk;
  }

  bool ParseSequenceHeader(const std::vector<uint8_t>& data) {
    EXPECT_TRUE(Init(data));
    return obu_->ParseSequenceHeader(/*seen_frame_header=*/false);
  }

  bool ParseFrameParameters(const std::vector<uint8_t>& data,
                            bool id_bits_present = false,
                            int force_screen_content_tools = 0,
                            int force_integer_mv = 0,
                            bool enable_superres = false) {
    EXPECT_TRUE(Init(data));
    if (id_bits_present) {
      obu_->sequence_header_.frame_id_numbers_present = true;
      obu_->sequence_header_.frame_id_length_bits = kFrameIdLengthBits;
      obu_->sequence_header_.delta_frame_id_length_bits =
          kDeltaFrameIdLengthBits;
    }
    obu_->sequence_header_.force_screen_content_tools =
        force_screen_content_tools;
    obu_->sequence_header_.force_integer_mv = force_integer_mv;
    obu_->sequence_header_.enable_superres = enable_superres;
    obu_->sequence_header_.frame_width_bits = kFrameWidthBits;
    obu_->sequence_header_.frame_height_bits = kFrameHeightBits;
    obu_->sequence_header_.max_frame_width = kWidth;
    obu_->sequence_header_.max_frame_height = kHeight;
    return obu_->ParseFrameParameters();
  }

  bool ParseSegmentationParameters(const std::vector<uint8_t>& data,
                                   int primary_reference_frame,
                                   int prev_frame_index) {
    EXPECT_TRUE(Init(data));
    obu_->frame_header_.primary_reference_frame = primary_reference_frame;
    if (primary_reference_frame != kPrimaryReferenceNone) {
      obu_->frame_header_.reference_frame_index[primary_reference_frame] =
          prev_frame_index;
    }
    return obu_->ParseSegmentationParameters();
  }

  bool ParseFrameReferenceModeSyntax(const std::vector<uint8_t>& data,
                                     FrameType frame_type) {
    EXPECT_TRUE(Init(data));
    obu_->frame_header_.frame_type = frame_type;
    return obu_->ParseFrameReferenceModeSyntax();
  }

  bool ParseGlobalMotionParameters(const std::vector<uint8_t>& data,
                                   FrameType frame_type) {
    EXPECT_TRUE(Init(data));
    obu_->frame_header_.frame_type = frame_type;
    obu_->frame_header_.primary_reference_frame = kPrimaryReferenceNone;
    return obu_->ParseGlobalMotionParameters();
  }

  bool ParseFilmGrainParameters(const std::vector<uint8_t>& data,
                                const ObuSequenceHeader& sequence_header,
                                const ObuFrameHeader& frame_header) {
    EXPECT_TRUE(Init(data));
    obu_->set_sequence_header(sequence_header);
    obu_->frame_header_ = frame_header;
    return obu_->ParseFilmGrainParameters();
  }

  bool ParseTileInfoSyntax(const std::vector<uint8_t>& data, int columns4x4,
                           int rows4x4, bool use_128x128_superblock) {
    EXPECT_TRUE(Init(data));
    obu_->frame_header_.columns4x4 = columns4x4;
    obu_->frame_header_.rows4x4 = rows4x4;
    obu_->sequence_header_.use_128x128_superblock = use_128x128_superblock;
    return obu_->ParseTileInfoSyntax();
  }

  bool ParseMetadata(const std::vector<uint8_t>& data) {
    EXPECT_TRUE(Init(data));
    return obu_->ParseMetadata(data.data(), data.size());
  }

  void DefaultSequenceHeader(ObuSequenceHeader* const gold) {
    memset(gold, 0, sizeof(*gold));
    gold->profile = kProfile0;
    gold->level[0].major = kMinimumMajorBitstreamLevel;
    gold->operating_points = 1;
    gold->max_frame_width = kWidth;
    gold->max_frame_height = kHeight;
    gold->frame_width_bits = kFrameWidthBits;
    gold->frame_height_bits = kFrameHeightBits;
    gold->use_128x128_superblock = true;
    gold->enable_filter_intra = true;
    gold->enable_intra_edge_filter = true;
    gold->enable_interintra_compound = true;
    gold->enable_masked_compound = true;
    gold->enable_dual_filter = true;
    gold->enable_order_hint = true;
    gold->enable_jnt_comp = true;
    gold->enable_ref_frame_mvs = true;
    gold->choose_screen_content_tools = true;
    gold->force_screen_content_tools = 2;
    gold->choose_integer_mv = true;
    gold->force_integer_mv = 2;
    gold->order_hint_bits = 7;
    gold->enable_cdef = true;
    gold->enable_restoration = true;
    gold->color_config.bitdepth = 8;
    gold->color_config.color_primary = kColorPrimaryUnspecified;
    gold->color_config.transfer_characteristics =
        kTransferCharacteristicsUnspecified;
    gold->color_config.matrix_coefficients = kMatrixCoefficientsUnspecified;
    gold->color_config.subsampling_x = 1;
    gold->color_config.subsampling_y = 1;
  }

  void DefaultFrameHeader(ObuFrameHeader* const gold, FrameType frame_type) {
    memset(gold, 0, sizeof(*gold));
    gold->frame_type = frame_type;
    gold->show_frame = true;
    gold->showable_frame = (frame_type != kFrameKey);
    gold->enable_cdf_update = true;
    gold->width = kWidth;
    gold->height = kHeight;
    gold->render_width = kWidth;
    gold->render_height = kHeight;
    gold->upscaled_width = kWidth;
    gold->primary_reference_frame = kPrimaryReferenceNone;
    gold->enable_frame_end_update_cdf = true;
    gold->rows4x4 = kRows4x4;
    gold->columns4x4 = kColumns4x4;
    if (frame_type == kFrameKey) {
      gold->refresh_frame_flags = 0xff;
      gold->error_resilient_mode = true;
      gold->force_integer_mv = 1;
    } else if (frame_type == kFrameIntraOnly) {
      gold->refresh_frame_flags = 4;
      gold->force_integer_mv = 1;
    } else if (frame_type == kFrameInter) {
      gold->refresh_frame_flags = 4;
      gold->primary_reference_frame = 1;
      for (int i = 0; i < kNumInterReferenceFrameTypes; ++i) {
        gold->reference_frame_index[i] = i;
      }
      gold->is_motion_mode_switchable = true;
    }
  }

  void OverrideFrameSize(BytesAndBits* const data, ObuFrameHeader* const gold,
                         int flag_offset, int size_offset) {
    data->SetBit(flag_offset, 1);  // frame_size_override_flag.
    data->InsertLiteral(size_offset, kFrameWidthBits,
                        kWidth - 2);  // frame_width_minus_1.
    data->InsertLiteral(size_offset + kFrameWidthBits, kFrameHeightBits,
                        kHeight - 2);  // frame_height_minus_1.
    gold->frame_size_override_flag = true;
    gold->width = kWidth - 1;
    gold->height = kHeight - 1;
    gold->render_width = gold->width;
    gold->render_height = gold->height;
    gold->upscaled_width = gold->width;
  }

  void OverrideRenderSize(BytesAndBits* const data, ObuFrameHeader* const gold,
                          int flag_offset) {
    data->SetBit(flag_offset, 1);  // render_and_frame_size_different.
    data->InsertLiteral(flag_offset + 1, 16,
                        kWidth - 10);  // render_width_minus_1.
    data->InsertLiteral(flag_offset + 17, 16,
                        kHeight - 10);  // render_height_minus_1.
    gold->render_width = kWidth - 9;
    gold->render_height = kHeight - 9;
    gold->render_and_frame_size_different = true;
  }

  void OverrideSegmentation(BytesAndBits* const data, Segmentation* const gold,
                            int offset) {
    gold->update_data = true;
    data->SetBit(offset++, static_cast<uint8_t>(gold->update_data));
    libvpx_test::ACMRandom rnd(libvpx_test::ACMRandom::DeterministicSeed());
    gold->segment_id_pre_skip = false;
    gold->last_active_segment_id = 0;
    for (int i = 0; i < kMaxSegments; ++i) {
      for (int j = 0; j < kSegmentFeatureMax; ++j) {
        gold->feature_enabled[i][j] = static_cast<bool>(rnd.Rand8() & 1);
        data->InsertBit(offset++,
                        static_cast<uint8_t>(gold->feature_enabled[i][j]));
        if (gold->feature_enabled[i][j]) {
          gold->feature_data[i][j] = rnd(1 << kSegmentationFeatureBits[j]);
          if (Segmentation::FeatureSigned(static_cast<SegmentFeature>(j))) {
            if (static_cast<bool>(rnd.Rand8() & 1)) {
              gold->feature_data[i][j] *= -1;
            }
            data->InsertInverseSignedLiteral(
                offset, kSegmentationFeatureBits[j], gold->feature_data[i][j]);
            offset += kSegmentationFeatureBits[j] + 1;
          } else {
            data->InsertLiteral(offset, kSegmentationFeatureBits[j],
                                gold->feature_data[i][j]);
            offset += kSegmentationFeatureBits[j];
          }
          gold->last_active_segment_id = i;
          if (j >= kSegmentFeatureReferenceFrame) {
            gold->segment_id_pre_skip = true;
          }
        }
      }
    }
  }

  void VerifyObuHeader(bool extension) {
    EXPECT_EQ(obu_->obu_headers().back().temporal_id, extension ? 6 : 0);
    EXPECT_EQ(obu_->obu_headers().back().spatial_id, extension ? 2 : 0);
  }

#define OBU_TEST_COMPARE(x) EXPECT_EQ(expected.x, actual.x)
  void VerifyFrameParameters(const ObuFrameHeader& expected,
                             bool id_bits_present = false) {
    const ObuFrameHeader& actual = obu_->frame_header();
    OBU_TEST_COMPARE(show_existing_frame);
    if (actual.show_existing_frame) {
      OBU_TEST_COMPARE(frame_to_show);
      OBU_TEST_COMPARE(frame_presentation_time);
      if (id_bits_present) {
        OBU_TEST_COMPARE(display_frame_id);
      }
      return;
    }
    OBU_TEST_COMPARE(frame_type);
    OBU_TEST_COMPARE(show_frame);
    OBU_TEST_COMPARE(frame_presentation_time);
    OBU_TEST_COMPARE(showable_frame);
    OBU_TEST_COMPARE(error_resilient_mode);
    OBU_TEST_COMPARE(enable_cdf_update);
    OBU_TEST_COMPARE(current_frame_id);
    OBU_TEST_COMPARE(frame_size_override_flag);
    OBU_TEST_COMPARE(order_hint);
    for (int i = 0; i < kNumReferenceFrameTypes; ++i) {
      OBU_TEST_COMPARE(reference_order_hint[i]);
    }
    OBU_TEST_COMPARE(primary_reference_frame);
    OBU_TEST_COMPARE(width);
    OBU_TEST_COMPARE(height);
    OBU_TEST_COMPARE(render_and_frame_size_different);
    OBU_TEST_COMPARE(render_width);
    OBU_TEST_COMPARE(render_height);
    OBU_TEST_COMPARE(upscaled_width);
    OBU_TEST_COMPARE(coded_lossless);
    OBU_TEST_COMPARE(upscaled_lossless);
    OBU_TEST_COMPARE(allow_screen_content_tools);
    OBU_TEST_COMPARE(is_motion_mode_switchable);
    OBU_TEST_COMPARE(refresh_frame_flags);
    OBU_TEST_COMPARE(enable_frame_end_update_cdf);
    OBU_TEST_COMPARE(force_integer_mv);
    if (actual.frame_type == kFrameInter) {
      for (int i = 0; i < kNumInterReferenceFrameTypes; ++i) {
        OBU_TEST_COMPARE(reference_frame_index[i]);
      }
    }
    OBU_TEST_COMPARE(use_superres);
    OBU_TEST_COMPARE(rows4x4);
    OBU_TEST_COMPARE(columns4x4);
  }

  void VerifyLoopFilterParameters(const LoopFilter& expected) {
    const LoopFilter& actual = obu_->frame_header().loop_filter;
    for (int i = 0; i < 4; ++i) {
      OBU_TEST_COMPARE(level[i]);
    }
    OBU_TEST_COMPARE(sharpness);
    OBU_TEST_COMPARE(delta_enabled);
    OBU_TEST_COMPARE(delta_update);
    for (int i = 0; i < kNumReferenceFrameTypes; ++i) {
      OBU_TEST_COMPARE(ref_deltas[i]);
    }
    for (int i = 0; i < kLoopFilterMaxModeDeltas; ++i) {
      OBU_TEST_COMPARE(mode_deltas[i]);
    }
  }

  void VerifyQuantizerParameters(const QuantizerParameters& expected) {
    const QuantizerParameters& actual = obu_->frame_header().quantizer;
    OBU_TEST_COMPARE(base_index);
    OBU_TEST_COMPARE(delta_dc[kPlaneY]);
    OBU_TEST_COMPARE(delta_dc[kPlaneU]);
    OBU_TEST_COMPARE(delta_dc[kPlaneV]);
    EXPECT_EQ(0, actual.delta_ac[kPlaneY]);
    OBU_TEST_COMPARE(delta_ac[kPlaneY]);
    OBU_TEST_COMPARE(delta_ac[kPlaneU]);
    OBU_TEST_COMPARE(delta_ac[kPlaneV]);
    OBU_TEST_COMPARE(use_matrix);
    OBU_TEST_COMPARE(matrix_level[kPlaneY]);
    OBU_TEST_COMPARE(matrix_level[kPlaneU]);
    OBU_TEST_COMPARE(matrix_level[kPlaneV]);
  }

  void VerifySegmentationParameters(const Segmentation& expected) {
    const Segmentation& actual = obu_->frame_header().segmentation;
    OBU_TEST_COMPARE(enabled);
    OBU_TEST_COMPARE(update_map);
    OBU_TEST_COMPARE(update_data);
    OBU_TEST_COMPARE(temporal_update);
    OBU_TEST_COMPARE(segment_id_pre_skip);
    OBU_TEST_COMPARE(last_active_segment_id);
    for (int i = 0; i < kMaxSegments; ++i) {
      for (int j = 0; j < kSegmentFeatureMax; ++j) {
        OBU_TEST_COMPARE(feature_enabled[i][j]);
        OBU_TEST_COMPARE(feature_data[i][j]);
      }
    }
  }

  void VerifyDeltaParameters(const Delta& expected, const Delta& actual) {
    OBU_TEST_COMPARE(present);
    OBU_TEST_COMPARE(scale);
    OBU_TEST_COMPARE(multi);
  }

  void VerifyCdefParameters(const Cdef& expected) {
    const Cdef& actual = obu_->frame_header().cdef;
    OBU_TEST_COMPARE(damping);
    OBU_TEST_COMPARE(bits);
    for (int i = 0; i < (1 << actual.bits); ++i) {
      OBU_TEST_COMPARE(y_primary_strength[i]);
      OBU_TEST_COMPARE(y_secondary_strength[i]);
      OBU_TEST_COMPARE(uv_primary_strength[i]);
      OBU_TEST_COMPARE(uv_secondary_strength[i]);
    }
  }

  void VerifyLoopRestorationParameters(const LoopRestoration& expected) {
    const LoopRestoration& actual = obu_->frame_header().loop_restoration;
    for (int i = 0; i < kMaxPlanes; ++i) {
      OBU_TEST_COMPARE(type[i]);
      OBU_TEST_COMPARE(unit_size_log2[i]);
    }
  }

  void VerifyGlobalMotionParameters(
      const std::array<GlobalMotion, kNumReferenceFrameTypes>& gold) {
    for (int i = kReferenceFrameLast; i <= kReferenceFrameAlternate; ++i) {
      const GlobalMotion& expected = gold[i];
      const GlobalMotion& actual = obu_->frame_header().global_motion[i];
      OBU_TEST_COMPARE(type) << " i: " << i;
      for (int j = 0; j < 6; ++j) {
        OBU_TEST_COMPARE(params[j]) << " i: " << i << " j: " << j;
      }
    }
  }

  void VerifyFilmGrainParameters(const FilmGrainParams& expected) {
    const FilmGrainParams& actual = obu_->frame_header().film_grain_params;
    OBU_TEST_COMPARE(apply_grain);
    OBU_TEST_COMPARE(update_grain);
    OBU_TEST_COMPARE(chroma_scaling_from_luma);
    OBU_TEST_COMPARE(overlap_flag);
    OBU_TEST_COMPARE(clip_to_restricted_range);
    OBU_TEST_COMPARE(num_y_points);
    OBU_TEST_COMPARE(num_u_points);
    OBU_TEST_COMPARE(num_v_points);
    for (int i = 0; i < 14; ++i) {
      OBU_TEST_COMPARE(point_y_value[i]);
      OBU_TEST_COMPARE(point_y_scaling[i]);
    }
    for (int i = 0; i < 10; ++i) {
      OBU_TEST_COMPARE(point_u_value[i]);
      OBU_TEST_COMPARE(point_u_scaling[i]);
    }
    for (int i = 0; i < 10; ++i) {
      OBU_TEST_COMPARE(point_v_value[i]);
      OBU_TEST_COMPARE(point_v_scaling[i]);
    }
    OBU_TEST_COMPARE(chroma_scaling);
    OBU_TEST_COMPARE(auto_regression_coeff_lag);
    for (int i = 0; i < 24; ++i) {
      OBU_TEST_COMPARE(auto_regression_coeff_y[i]);
    }
    for (int i = 0; i < 25; ++i) {
      OBU_TEST_COMPARE(auto_regression_coeff_u[i]);
    }
    for (int i = 0; i < 25; ++i) {
      OBU_TEST_COMPARE(auto_regression_coeff_v[i]);
    }
    OBU_TEST_COMPARE(auto_regression_shift);
    OBU_TEST_COMPARE(grain_seed);
    OBU_TEST_COMPARE(reference_index);
    OBU_TEST_COMPARE(grain_scale_shift);
    OBU_TEST_COMPARE(u_multiplier);
    OBU_TEST_COMPARE(u_luma_multiplier);
    OBU_TEST_COMPARE(u_offset);
    OBU_TEST_COMPARE(v_multiplier);
    OBU_TEST_COMPARE(v_luma_multiplier);
    OBU_TEST_COMPARE(v_offset);
  }

  void VerifyTileInfoParameters(const TileInfo& expected) {
    const TileInfo& actual = obu_->frame_header().tile_info;
    OBU_TEST_COMPARE(uniform_spacing);
    OBU_TEST_COMPARE(tile_columns_log2);
    OBU_TEST_COMPARE(tile_columns);
    for (int i = 0; i < kMaxTileColumns + 1; ++i) {
      OBU_TEST_COMPARE(tile_column_start[i]) << "tile_column: " << i;
      OBU_TEST_COMPARE(tile_column_width_in_superblocks[i])
          << "tile_column: " << i;
    }
    OBU_TEST_COMPARE(tile_rows_log2);
    OBU_TEST_COMPARE(tile_rows);
    for (int i = 0; i < kMaxTileRows + 1; ++i) {
      OBU_TEST_COMPARE(tile_row_start[i]) << "tile_row: " << i;
      OBU_TEST_COMPARE(tile_row_height_in_superblocks[i]) << "tile_rows: " << i;
    }
    OBU_TEST_COMPARE(tile_count);
    OBU_TEST_COMPARE(context_update_id);
    OBU_TEST_COMPARE(tile_size_bytes);
  }

  void VerifySequenceHeader(const ObuSequenceHeader& expected) {
    EXPECT_TRUE(obu_->sequence_header_changed());
    const ObuSequenceHeader& actual = obu_->sequence_header();
    OBU_TEST_COMPARE(profile);
    OBU_TEST_COMPARE(still_picture);
    OBU_TEST_COMPARE(reduced_still_picture_header);
    OBU_TEST_COMPARE(operating_points);
    for (int i = 0; i < actual.operating_points; ++i) {
      OBU_TEST_COMPARE(operating_point_idc[i]) << "i: " << i;
      OBU_TEST_COMPARE(level[i].major) << "i: " << i;
      OBU_TEST_COMPARE(level[i].minor) << "i: " << i;
      OBU_TEST_COMPARE(tier[i]) << "i: " << i;
    }
    OBU_TEST_COMPARE(frame_width_bits);
    OBU_TEST_COMPARE(frame_height_bits);
    OBU_TEST_COMPARE(max_frame_width);
    OBU_TEST_COMPARE(max_frame_height);
    OBU_TEST_COMPARE(frame_id_numbers_present);
    if (actual.frame_id_numbers_present) {
      OBU_TEST_COMPARE(frame_id_length_bits);
      OBU_TEST_COMPARE(delta_frame_id_length_bits);
    }
    OBU_TEST_COMPARE(use_128x128_superblock);
    OBU_TEST_COMPARE(enable_filter_intra);
    OBU_TEST_COMPARE(enable_intra_edge_filter);
    OBU_TEST_COMPARE(enable_interintra_compound);
    OBU_TEST_COMPARE(enable_masked_compound);
    OBU_TEST_COMPARE(enable_warped_motion);
    OBU_TEST_COMPARE(enable_dual_filter);
    OBU_TEST_COMPARE(enable_order_hint);
    OBU_TEST_COMPARE(enable_jnt_comp);
    OBU_TEST_COMPARE(enable_ref_frame_mvs);
    OBU_TEST_COMPARE(choose_screen_content_tools);
    OBU_TEST_COMPARE(force_screen_content_tools);
    OBU_TEST_COMPARE(choose_integer_mv);
    OBU_TEST_COMPARE(force_integer_mv);
    OBU_TEST_COMPARE(order_hint_bits);
    OBU_TEST_COMPARE(enable_superres);
    OBU_TEST_COMPARE(enable_cdef);
    OBU_TEST_COMPARE(enable_restoration);
    OBU_TEST_COMPARE(color_config.bitdepth);
    OBU_TEST_COMPARE(color_config.is_monochrome);
    OBU_TEST_COMPARE(color_config.color_range);
    OBU_TEST_COMPARE(color_config.subsampling_x);
    OBU_TEST_COMPARE(color_config.subsampling_y);
    OBU_TEST_COMPARE(color_config.chroma_sample_position);
    OBU_TEST_COMPARE(timing_info_present_flag);
    OBU_TEST_COMPARE(timing_info.num_units_in_tick);
    OBU_TEST_COMPARE(timing_info.time_scale);
    OBU_TEST_COMPARE(timing_info.equal_picture_interval);
    OBU_TEST_COMPARE(timing_info.num_ticks_per_picture);
    OBU_TEST_COMPARE(decoder_model_info_present_flag);
    OBU_TEST_COMPARE(decoder_model_info.encoder_decoder_buffer_delay_length);
    OBU_TEST_COMPARE(decoder_model_info.num_units_in_decoding_tick);
    OBU_TEST_COMPARE(decoder_model_info.buffer_removal_time_length);
    OBU_TEST_COMPARE(decoder_model_info.frame_presentation_time_length);
    for (int i = 0; i < actual.operating_points; ++i) {
      SCOPED_TRACE("i: " + std::to_string(i));
      OBU_TEST_COMPARE(operating_parameters.decoder_buffer_delay[i]);
      OBU_TEST_COMPARE(operating_parameters.encoder_buffer_delay[i]);
      OBU_TEST_COMPARE(operating_parameters.low_delay_mode_flag[i]);
      OBU_TEST_COMPARE(initial_display_delay[i]);
    }
    OBU_TEST_COMPARE(film_grain_params_present);
  }

  void VerifyMetadataHdrCll(const ObuMetadataHdrCll& expected) {
    EXPECT_TRUE(obu_->current_frame_->hdr_cll_set());
    const ObuMetadataHdrCll& actual = obu_->current_frame_->hdr_cll();
    OBU_TEST_COMPARE(max_cll);
    OBU_TEST_COMPARE(max_fall);
  }

  void VerifyMetadataHdrMdcv(const ObuMetadataHdrMdcv& expected) {
    EXPECT_TRUE(obu_->current_frame_->hdr_mdcv_set());
    const ObuMetadataHdrMdcv& actual = obu_->current_frame_->hdr_mdcv();
    for (int i = 0; i < 3; ++i) {
      OBU_TEST_COMPARE(primary_chromaticity_x[i]);
      OBU_TEST_COMPARE(primary_chromaticity_y[i]);
    }
    OBU_TEST_COMPARE(white_point_chromaticity_x);
    OBU_TEST_COMPARE(white_point_chromaticity_y);
    OBU_TEST_COMPARE(luminance_max);
    OBU_TEST_COMPARE(luminance_min);
  }

  void VerifyMetadataItutT35(const ObuMetadataItutT35& expected) {
    EXPECT_TRUE(obu_->current_frame_->itut_t35_set());
    const ObuMetadataItutT35& actual = obu_->current_frame_->itut_t35();
    OBU_TEST_COMPARE(country_code);
    if (actual.country_code == 0xFF) {
      OBU_TEST_COMPARE(country_code_extension_byte);
    }
    ASSERT_EQ(expected.payload_size, actual.payload_size);
    if (actual.payload_size != 0) {
      EXPECT_EQ(memcmp(expected.payload_bytes, actual.payload_bytes,
                       actual.payload_size),
                0);
    }
  }

#undef OBU_TEST_COMPARE

  // Accessors to private members of ObuParser. This avoids the need for a
  // dependency on a googletest header in the main library for FRIEND_TEST()
  // (or the need to duplicate the implementation).
  bool ObuParseFrameParameters() { return obu_->ParseFrameParameters(); }
  bool ObuParseLoopFilterParameters() {
    return obu_->ParseLoopFilterParameters();
  }
  bool ObuParseLoopFilterDeltaParameters() {
    return obu_->ParseLoopFilterDeltaParameters();
  }
  bool ObuParseQuantizerParameters() {
    return obu_->ParseQuantizerParameters();
  }
  bool ObuParseQuantizerIndexDeltaParameters() {
    return obu_->ParseQuantizerIndexDeltaParameters();
  }
  void ObuComputeSegmentLosslessAndQIndex() {
    obu_->ComputeSegmentLosslessAndQIndex();
  }
  bool ObuParseCdefParameters() { return obu_->ParseCdefParameters(); }
  bool ObuParseLoopRestorationParameters() {
    return obu_->ParseLoopRestorationParameters();
  }
  bool ObuParseTxModeSyntax() { return obu_->ParseTxModeSyntax(); }
  bool ObuIsSkipModeAllowed() { return obu_->IsSkipModeAllowed(); }
  bool ObuParseSkipModeParameters() { return obu_->ParseSkipModeParameters(); }
  bool ObuReadAllowWarpedMotion() { return obu_->ReadAllowWarpedMotion(); }
  bool ObuSetFrameReferences(int8_t last_frame_idx, int8_t gold_frame_idx) {
    return obu_->SetFrameReferences(last_frame_idx, gold_frame_idx);
  }

  std::unique_ptr<BufferPool> buffer_pool_;
  DecoderState decoder_state_;
  std::unique_ptr<ObuParser> obu_;
  // The following members are reset with each Init().
  Vector<ObuHeader>* obu_headers_;
  ObuFrameHeader* obu_frame_header_;
  ObuSequenceHeader* obu_sequence_header_;
  RefCountedBufferPtr current_frame_;
};

TEST_F(ObuParserTest, InvalidInputs) {
  obu_.reset(new (std::nothrow)
                 ObuParser(nullptr, 0, 0, buffer_pool_.get(), &decoder_state_));
  EXPECT_EQ(obu_->ParseOneFrame(&current_frame_), kStatusInvalidArgument);
  obu_.reset(new (std::nothrow) ObuParser(nullptr, 10, 0, buffer_pool_.get(),
                                          &decoder_state_));
  EXPECT_EQ(obu_->ParseOneFrame(&current_frame_), kStatusInvalidArgument);
  obu_.reset(new (std::nothrow)
                 ObuParser(kDefaultTemporalDelimiter.data(), 0, 0,
                           buffer_pool_.get(), &decoder_state_));
  EXPECT_EQ(obu_->ParseOneFrame(&current_frame_), kStatusInvalidArgument);
}

TEST_F(ObuParserTest, TemporalDelimiter) {
  BytesAndBits data;
  data.AppendBytes(kDefaultTemporalDelimiter);

  ASSERT_TRUE(Parse(data.GenerateData()));
  EXPECT_EQ(obu_->obu_headers().size(), 1);
  EXPECT_EQ(obu_->obu_headers().back().type, kObuTemporalDelimiter);
  VerifyObuHeader(false);

  // forbidden_bit is not zero.
  data.SetBit(0, 1);
  EXPECT_FALSE(Parse(data.GenerateData()));
}

TEST_F(ObuParserTest, HeaderExtensions) {
  BytesAndBits data;
  data.AppendBytes(kDefaultTemporalDelimiterWithExtension);

  ASSERT_TRUE(Parse(data.GenerateData()));
  EXPECT_EQ(obu_->obu_headers().size(), 1);
  EXPECT_EQ(obu_->obu_headers().back().type, kObuTemporalDelimiter);
  VerifyObuHeader(true);

  // extension flag is set but no extensions found.
  data.Clear();
  data.AppendByte(kDefaultTemporalDelimiterWithExtension[0]);
  EXPECT_FALSE(Parse(data.GenerateData()));
}

TEST_F(ObuParserTest, HeaderHasSizeFieldNotSet) {
  BytesAndBits data;
  data.AppendBytes(kDefaultHeaderWithoutSizeField);

  EXPECT_FALSE(Parse(data.GenerateData()));
}

TEST_F(ObuParserTest, SequenceHeader) {
  BytesAndBits data;
  data.AppendBytes(kDefaultSequenceHeader);
  ObuSequenceHeader gold;
  DefaultSequenceHeader(&gold);

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);
}

TEST_F(ObuParserTest, SequenceHeaderLevel) {
  BytesAndBits data;
  data.AppendBytes(kDefaultSequenceHeader);
  ObuSequenceHeader gold;
  DefaultSequenceHeader(&gold);

  // Set level to 1.
  gold.level[0].major = 2;
  gold.level[0].minor = 1;
  data.SetLiteral(24, 5, 1);  // level.

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);

  // Set operating_point_idc of operating point 1 to 0x101 (temporal layer 0
  // and spatial layer 0 should be decoded). Set level of operating point 1 to
  // 8 (4.0) and tier to 1.
  gold.operating_points = 2;
  gold.operating_point_idc[1] = (1 << 0) | (1 << (0 + 8));
  gold.level[1].major = 4;
  gold.level[1].minor = 0;
  gold.tier[1] = 1;
  data.SetLiteral(7, 5, gold.operating_points - 1);
  data.InsertLiteral(29, 12, 0x101);  // operating_point_idc.
  data.InsertLiteral(41, 5, 8);       // level.
  data.InsertBit(46, gold.tier[1]);

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);
}

TEST_F(ObuParserTest, SequenceHeaderProfile) {
  BytesAndBits data;
  data.AppendBytes(kDefaultSequenceHeader);
  ObuSequenceHeader gold;
  DefaultSequenceHeader(&gold);

  gold.still_picture = true;
  data.SetBit(3, static_cast<uint8_t>(gold.still_picture));

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);

  // profile 2; bitdepth 8;
  gold.profile = kProfile2;
  gold.color_config.bitdepth = 8;
  gold.color_config.subsampling_x = 1;
  gold.color_config.subsampling_y = 0;
  data.SetLiteral(0, 3, gold.profile);

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);

  // profile 2; bitdepth 10;
  gold.color_config.bitdepth = 10;
  data.SetBit(73, 1);     // high_bitdepth.
  data.InsertBit(74, 0);  // twelve_bit.

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);

  // profile 2; bitdepth 12;
  gold.color_config.bitdepth = 12;
  gold.color_config.subsampling_y = 1;
  data.SetBit(74, 1);     // twelve_bit.
  data.InsertBit(78, 1);  // subsampling_x.
  data.InsertBit(79, 1);  // subsampling_y.

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);
}

TEST_F(ObuParserTest, SequenceHeaderIdLength) {
  BytesAndBits data;
  data.AppendBytes(kDefaultSequenceHeader);
  ObuSequenceHeader gold;
  DefaultSequenceHeader(&gold);

  gold.frame_id_numbers_present = true;
  gold.delta_frame_id_length_bits = kDeltaFrameIdLengthBits;
  gold.frame_id_length_bits = kFrameIdLengthBits;
  data.SetBit(54, 1);  // frame_id_numbers_present.
  data.InsertLiteral(55, 4, kDeltaFrameIdLengthBits - 2);
  data.InsertLiteral(59, 3, kFrameIdLengthBits - kDeltaFrameIdLengthBits - 1);

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);
}

// An idLen greater than 16 is invalid.
TEST_F(ObuParserTest, SequenceHeaderIdLengthInvalid) {
  BytesAndBits data;
  data.AppendBytes(kDefaultSequenceHeader);

  data.SetBit(54, 1);  // frame_id_numbers_present.
  data.InsertLiteral(55, 4, kDeltaFrameIdLengthBits - 2);
  data.InsertLiteral(59, 3, 17 - kDeltaFrameIdLengthBits - 1);  // idLen = 17.

  ASSERT_FALSE(ParseSequenceHeader(data.GenerateData()));
}

TEST_F(ObuParserTest, SequenceHeaderFlags) {
  BytesAndBits data;
  data.AppendBytes(kDefaultSequenceHeader);
  ObuSequenceHeader gold;
  DefaultSequenceHeader(&gold);

  gold.enable_warped_motion = true;
  gold.enable_superres = true;
  data.SetBit(60, 1);  // enable_warped_motion.
  data.SetBit(70, 1);  // enable_superres.

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);
}

TEST_F(ObuParserTest, SequenceHeaderForceScreenContentToolsEqualTo0) {
  BytesAndBits data;
  data.AppendBytes(kDefaultSequenceHeader);
  ObuSequenceHeader gold;
  DefaultSequenceHeader(&gold);

  gold.choose_screen_content_tools = false;
  gold.force_screen_content_tools = 0;
  gold.choose_integer_mv = false;
  gold.force_integer_mv = 2;
  data.SetBit(65, 0);  // choose_screen_content_tools.
  data.SetBit(66, 0);  // force_screen_content_tools.

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);
}

TEST_F(ObuParserTest, SequenceHeaderMonochrome) {
  BytesAndBits data;
  data.AppendBytes(kDefaultSequenceHeader);
  ObuSequenceHeader gold;
  DefaultSequenceHeader(&gold);

  gold.color_config.is_monochrome = true;
  gold.color_config.color_range = kColorRangeFull;
  data.SetBit(74, 1);     // monochrome.
  data.InsertBit(76, 1);  // color_range.

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);
}

// This tests TimingInfo, DecoderModelInfo and OperatingParameters. The test is
// kind of long but it is the simplest way to test all three since they are
// dependent on one another.
TEST_F(ObuParserTest, SequenceHeaderTimingInfo) {
  BytesAndBits data;
  data.AppendBytes(kDefaultSequenceHeader);
  ObuSequenceHeader gold;
  DefaultSequenceHeader(&gold);

  gold.timing_info_present_flag = true;
  gold.timing_info.num_units_in_tick = 100;
  gold.timing_info.time_scale = 1000;
  gold.timing_info.equal_picture_interval = false;
  gold.decoder_model_info_present_flag = false;
  data.SetBit(5, static_cast<uint8_t>(gold.timing_info_present_flag));
  data.InsertLiteral(6, 32, gold.timing_info.num_units_in_tick);
  data.InsertLiteral(38, 32, gold.timing_info.time_scale);
  data.InsertBit(70,
                 static_cast<uint8_t>(gold.timing_info.equal_picture_interval));
  data.InsertBit(71,
                 static_cast<uint8_t>(gold.decoder_model_info_present_flag));

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);

  gold.timing_info.equal_picture_interval = true;
  gold.timing_info.num_ticks_per_picture = 7;
  data.SetBit(70,
              static_cast<uint8_t>(gold.timing_info.equal_picture_interval));
  EXPECT_EQ(data.InsertUvlc(71, gold.timing_info.num_ticks_per_picture - 1), 5);

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);

  gold.decoder_model_info_present_flag = true;
  gold.decoder_model_info.encoder_decoder_buffer_delay_length = 5;
  gold.decoder_model_info.num_units_in_decoding_tick = 1000;
  gold.decoder_model_info.buffer_removal_time_length = 18;
  gold.decoder_model_info.frame_presentation_time_length = 20;

  data.SetBit(76, static_cast<uint8_t>(gold.decoder_model_info_present_flag));
  data.InsertLiteral(
      77, 5, gold.decoder_model_info.encoder_decoder_buffer_delay_length - 1);
  data.InsertLiteral(82, 32,
                     gold.decoder_model_info.num_units_in_decoding_tick);
  data.InsertLiteral(114, 5,
                     gold.decoder_model_info.buffer_removal_time_length - 1);
  data.InsertLiteral(
      119, 5, gold.decoder_model_info.frame_presentation_time_length - 1);
  data.InsertBit(147, 0);  // decoder_model_present_for_this_op.

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);

  gold.operating_parameters.decoder_buffer_delay[0] = 10;
  gold.operating_parameters.encoder_buffer_delay[0] = 20;
  gold.operating_parameters.low_delay_mode_flag[0] = true;

  data.SetBit(147, 1);  // decoder_model_present_for_this_op.
  data.InsertLiteral(
      148, gold.decoder_model_info.encoder_decoder_buffer_delay_length,
      gold.operating_parameters.decoder_buffer_delay[0]);
  data.InsertLiteral(
      153, gold.decoder_model_info.encoder_decoder_buffer_delay_length,
      gold.operating_parameters.encoder_buffer_delay[0]);
  data.InsertBit(158, static_cast<uint8_t>(
                          gold.operating_parameters.low_delay_mode_flag[0]));

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);
}

TEST_F(ObuParserTest, SequenceHeaderInitialDisplayDelay) {
  BytesAndBits data;
  data.AppendBytes(kDefaultSequenceHeader);
  ObuSequenceHeader gold;
  DefaultSequenceHeader(&gold);

  gold.initial_display_delay[0] = 8;

  data.SetBit(6, 1);      // initial_display_delay_present_flag.
  data.InsertBit(29, 1);  // initial_display_delay_present_for_this_op.
  data.InsertLiteral(30, 4, gold.initial_display_delay[0] - 1);

  ASSERT_TRUE(ParseSequenceHeader(data.GenerateData()));
  VerifySequenceHeader(gold);
}

// Parsing of a frame header should fail if no sequence header has been
// received.
TEST_F(ObuParserTest, FrameHeaderWithoutSequenceHeader) {
  // The aom-test-data test vector av1-1-b8-01-size-16x16.ivf has two temporal
  // units. The first temporal unit has a presentation timestamp of 0 and
  // consists of three OBUs: a temporal delimiter OBU, a sequence header OBU,
  // and a frame OBU.
  const std::vector<uint8_t> kTemporalDelimiter = {0x12, 0x00};
  const std::vector<uint8_t> kSequenceHeader = {
      0x0a, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x9f, 0xfb, 0xff, 0xf3, 0x00, 0x80};
  const std::vector<uint8_t> kFrame = {
      0x32, 0xa6, 0x01, 0x10, 0x00, 0x87, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00,
      0x40, 0x00, 0x9e, 0x86, 0x5b, 0xb2, 0x22, 0xb5, 0x58, 0x4d, 0x68, 0xe6,
      0x37, 0x54, 0x42, 0x7b, 0x84, 0xce, 0xdf, 0x9f, 0xec, 0xab, 0x07, 0x4d,
      0xf6, 0xe1, 0x5e, 0x9e, 0x27, 0xbf, 0x93, 0x2f, 0x47, 0x0d, 0x7b, 0x7c,
      0x45, 0x8d, 0xcf, 0x26, 0xf7, 0x6c, 0x06, 0xd7, 0x8c, 0x2e, 0xf5, 0x2c,
      0xb0, 0x8a, 0x31, 0xac, 0x69, 0xf5, 0xcd, 0xd8, 0x71, 0x5d, 0xaf, 0xf8,
      0x96, 0x43, 0x8c, 0x9c, 0x23, 0x6f, 0xab, 0xd0, 0x35, 0x43, 0xdf, 0x81,
      0x12, 0xe3, 0x7d, 0xec, 0x22, 0xb0, 0x30, 0x54, 0x32, 0x9f, 0x90, 0xc0,
      0x5d, 0x64, 0x9b, 0x0f, 0x75, 0x31, 0x84, 0x3a, 0x57, 0xd7, 0x5f, 0x03,
      0x6e, 0x7f, 0x43, 0x17, 0x6d, 0x08, 0xc3, 0x81, 0x8a, 0xae, 0x73, 0x1c,
      0xa8, 0xa7, 0xe4, 0x9c, 0xa9, 0x5b, 0x3f, 0xd1, 0xeb, 0x75, 0x3a, 0x7f,
      0x22, 0x77, 0x38, 0x64, 0x1c, 0x77, 0xdb, 0xcd, 0xef, 0xb7, 0x08, 0x45,
      0x8e, 0x7f, 0xea, 0xa3, 0xd0, 0x81, 0xc9, 0xc1, 0xbc, 0x93, 0x9b, 0x41,
      0xb1, 0xa1, 0x42, 0x17, 0x98, 0x3f, 0x1e, 0x95, 0xdf, 0x68, 0x7c, 0xb7,
      0x98};

  BytesAndBits data;
  data.AppendBytes(kTemporalDelimiter);
  // Skip the sequence header OBU.
  data.AppendBytes(kFrame);
  ASSERT_FALSE(Parse(data.GenerateData()));

  // Now verify that all three OBUs are correct, by adding them to |data|
  // successively.
  data.Clear();
  data.AppendBytes(kTemporalDelimiter);
  ASSERT_TRUE(Parse(data.GenerateData()));
  data.Clear();
  data.AppendBytes(kTemporalDelimiter);
  data.AppendBytes(kSequenceHeader);
  ASSERT_TRUE(Parse(data.GenerateData()));
  data.Clear();
  data.AppendBytes(kTemporalDelimiter);
  data.AppendBytes(kSequenceHeader);
  data.AppendBytes(kFrame);
  ASSERT_TRUE(Parse(data.GenerateData()));
}

TEST_F(ObuParserTest, FrameParameterShowExistingFrame) {
  BytesAndBits data;
  data.AppendBit(1);                    // show_existing_frame.
  data.AppendLiteral(3, kFrameToShow);  // frame_to_show.
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameKey);
  gold.show_existing_frame = true;
  gold.frame_to_show = kFrameToShow;

  // kFrameToShow'th frame is not yet decoded.
  ASSERT_FALSE(ParseFrameParameters(data.GenerateData()));

  decoder_state_.reference_frame[kFrameToShow] = buffer_pool_->GetFreeBuffer();
  // kFrameToShow'th frame is not a showable frame.
  ASSERT_FALSE(ParseFrameParameters(data.GenerateData()));

  decoder_state_.reference_frame[kFrameToShow]->set_showable_frame(true);
  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);
}

TEST_F(ObuParserTest, FrameParametersShowExistingFrameWithDisplayFrameId) {
  BytesAndBits data;
  data.AppendBit(1);                        // show_existing_frame.
  data.AppendLiteral(3, kFrameToShow);      // frame_to_show.
  data.AppendLiteral(15, kDisplayFrameId);  // display_frame_id.
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameKey);
  gold.show_existing_frame = true;
  gold.frame_to_show = kFrameToShow;
  gold.display_frame_id = kDisplayFrameId;

  // kFrameToShow'th frame is not yet decoded.
  ASSERT_FALSE(ParseFrameParameters(data.GenerateData(), true));

  decoder_state_.reference_frame_id[kFrameToShow] = kDisplayFrameId;
  decoder_state_.reference_frame[kFrameToShow] = buffer_pool_->GetFreeBuffer();
  // kFrameToShow'th frame is not a showable frame.
  ASSERT_FALSE(ParseFrameParameters(data.GenerateData(), true));

  decoder_state_.reference_frame[kFrameToShow]->set_showable_frame(true);
  ASSERT_TRUE(ParseFrameParameters(data.GenerateData(), true));
  VerifyFrameParameters(gold, true);
}

TEST_F(ObuParserTest, FrameParameterShowExistingFrameTemporalPointInfo) {
  BytesAndBits data;
  data.AppendBit(1);                    // show_existing_frame.
  data.AppendLiteral(3, kFrameToShow);  // frame_to_show.
  data.AppendLiteral(20, 38);           // frame_presentation_time.
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameKey);
  gold.show_existing_frame = true;
  gold.frame_to_show = kFrameToShow;
  gold.frame_presentation_time = 38;

  EXPECT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->frame_width_bits = kFrameWidthBits;
  obu_sequence_header_->frame_height_bits = kFrameHeightBits;
  obu_sequence_header_->max_frame_width = kWidth;
  obu_sequence_header_->max_frame_height = kHeight;

  obu_sequence_header_->decoder_model_info_present_flag = true;
  obu_sequence_header_->decoder_model_info.frame_presentation_time_length = 20;

  decoder_state_.reference_frame[kFrameToShow] = buffer_pool_->GetFreeBuffer();
  decoder_state_.reference_frame[kFrameToShow]->set_showable_frame(true);

  ASSERT_TRUE(ObuParseFrameParameters());
  VerifyFrameParameters(gold);
}

TEST_F(ObuParserTest, FrameParameterErrorResilientMode) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderIntraOnlyFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameIntraOnly);

  gold.error_resilient_mode = true;
  data.SetBit(4, static_cast<uint8_t>(gold.error_resilient_mode));

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);
}

TEST_F(ObuParserTest, FrameParameterKeyFrame) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderKeyFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameKey);

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);
}

TEST_F(ObuParserTest, FrameParameterKeyFrameTemporalPointInfo) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderKeyFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameKey);

  data.InsertLiteral(4, 20, 38);  // frame_presentation_time.
  gold.frame_presentation_time = 38;

  EXPECT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->frame_width_bits = kFrameWidthBits;
  obu_sequence_header_->frame_height_bits = kFrameHeightBits;
  obu_sequence_header_->max_frame_width = kWidth;
  obu_sequence_header_->max_frame_height = kHeight;

  obu_sequence_header_->decoder_model_info_present_flag = true;
  obu_sequence_header_->decoder_model_info.frame_presentation_time_length = 20;

  ASSERT_TRUE(ObuParseFrameParameters());
  VerifyFrameParameters(gold);
}

TEST_F(ObuParserTest, FrameParameterKeyFrameOverrideSize) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderKeyFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameKey);

  OverrideFrameSize(&data, &gold, 5, 6);

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);

  OverrideRenderSize(&data, &gold, 23);

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);
}

TEST_F(ObuParserTest, FrameParameterKeyFrameSuperRes) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderKeyFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameKey);
  gold.use_superres = true;
  gold.superres_scale_denominator = 15;
  gold.width = kWidth * 8 / 15;
  gold.columns4x4 = 58;

  data.SetBit(6, static_cast<int>(gold.use_superres));
  data.SetLiteral(7, 3, gold.superres_scale_denominator - 9);

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData(), false, 0, 0, true));
  VerifyFrameParameters(gold);
}

TEST_F(ObuParserTest, FrameParameterKeyFrameAllowScreenContentTools) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderKeyFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameKey);

  data.InsertBit(5, 1);  // allow_screen_content_tools.
  data.InsertBit(8, 1);  // allow_intrabc.
  gold.allow_screen_content_tools = true;
  gold.allow_intrabc = true;

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData(), false, 2));
  VerifyFrameParameters(gold);

  data.InsertBit(6, 1);  // force_integer_mv.
  gold.force_integer_mv = 1;

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData(), false, 2, 2));
  VerifyFrameParameters(gold);

  data.SetBit(6, 0);  // force_integer_mv.

  // Gold need not be updated, because force_integer_mv is always 1 for
  // keyframes.
  ASSERT_TRUE(ParseFrameParameters(data.GenerateData(), false, 2, 2));
  VerifyFrameParameters(gold);
}

TEST_F(ObuParserTest, FrameParameterIntraOnlyFrame) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderIntraOnlyFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameIntraOnly);

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);
}

TEST_F(ObuParserTest, FrameParameterIntraOnlyFrameOverrideSize) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderIntraOnlyFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameIntraOnly);

  OverrideFrameSize(&data, &gold, 6, 15);

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);

  OverrideRenderSize(&data, &gold, 32);

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);
}

// An INTRA_ONLY_FRAME cannot set refresh_frame_flags to 0xff.
TEST_F(ObuParserTest, FrameParameterIntraOnlyFrameRefreshAllFrames) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderIntraOnlyFrame);
  data.SetLiteral(7, 8, 0xFF);  // refresh_frame_flags.

  ASSERT_FALSE(ParseFrameParameters(data.GenerateData()));
}

TEST_F(ObuParserTest, FrameParameterInterFrame) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderInterFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameInter);
  ObuFrameHeader reference_frame_header;
  reference_frame_header.width = kWidth;
  reference_frame_header.height = kHeight;
  reference_frame_header.render_width = kWidth;
  reference_frame_header.render_height = kHeight;
  reference_frame_header.upscaled_width = kWidth;
  reference_frame_header.rows4x4 = kRows4x4;
  reference_frame_header.columns4x4 = kColumns4x4;
  reference_frame_header.refresh_frame_flags = 0;
  for (auto& reference_frame : decoder_state_.reference_frame) {
    reference_frame = buffer_pool_->GetFreeBuffer();
    EXPECT_TRUE(reference_frame->SetFrameDimensions(reference_frame_header));
  }

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);
}

TEST_F(ObuParserTest, FrameParameterInterFrameOverrideSize) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderInterFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameInter);
  ObuFrameHeader reference_frame_header;
  reference_frame_header.width = kWidth;
  reference_frame_header.height = kHeight;
  reference_frame_header.render_width = kWidth;
  reference_frame_header.render_height = kHeight;
  reference_frame_header.upscaled_width = kWidth;
  reference_frame_header.rows4x4 = kRows4x4;
  reference_frame_header.columns4x4 = kColumns4x4;
  reference_frame_header.refresh_frame_flags = 0;
  for (auto& reference_frame : decoder_state_.reference_frame) {
    reference_frame = buffer_pool_->GetFreeBuffer();
    EXPECT_TRUE(reference_frame->SetFrameDimensions(reference_frame_header));
  }

  data.InsertLiteral(39, kNumInterReferenceFrameTypes, 0);  // found_ref.
  OverrideFrameSize(&data, &gold, 6, 46);

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);

  OverrideRenderSize(&data, &gold, 63);

  ASSERT_TRUE(ParseFrameParameters(data.GenerateData()));
  VerifyFrameParameters(gold);
}

// This test verifies we check the following requirement at the end of Section
// 6.8.4:
//   If FrameIsIntra is equal to 0 (indicating that this frame may use inter
//   prediction), the requirements described in the frame size with refs
//   semantics of section 6.8.6 must also be satisfied.
TEST_F(ObuParserTest, FrameParameterInterFrameInvalidSize) {
  BytesAndBits data;
  data.AppendBytes(kDefaultFrameHeaderInterFrame);
  ObuFrameHeader gold;
  DefaultFrameHeader(&gold, kFrameInter);
  ObuFrameHeader reference_frame_header;
  reference_frame_header.width = kWidth;
  reference_frame_header.height = 2 * kHeight + 8;
  reference_frame_header.render_width = kWidth;
  reference_frame_header.render_height = 2 * kHeight + 8;
  reference_frame_header.upscaled_width = kWidth;
  reference_frame_header.rows4x4 = 2 * kRows4x4 + 2;
  reference_frame_header.columns4x4 = kColumns4x4;
  reference_frame_header.refresh_frame_flags = 0;
  for (auto& reference_frame : decoder_state_.reference_frame) {
    reference_frame = buffer_pool_->GetFreeBuffer();
    EXPECT_TRUE(reference_frame->SetFrameDimensions(reference_frame_header));
  }

  EXPECT_FALSE(ParseFrameParameters(data.GenerateData()));
}

// Tests the ObuParser::SetFrameReferences() method.
//
// This method uses the following data members as input:
//   decoder_state_.reference_order_hint
//   sequence_header_.enable_order_hint
//   sequence_header_.order_hint_bits
//   frame_header_.order_hint
// So we need to set up these data members before calling
// ObuParser::SetFrameReferences().
//
// The output is in frame_header_.reference_frame_index.
TEST_F(ObuParserTest, SetFrameReferences) {
  // All reference frames are forward references (because 9 < 17).
  for (int i = 0; i < kNumReferenceFrameTypes; ++i) {
    decoder_state_.reference_order_hint[i] = 9;
  }

  ASSERT_TRUE(Init());
  obu_sequence_header_->enable_order_hint = true;
  obu_sequence_header_->order_hint_bits = 5;
  obu_sequence_header_->order_hint_shift_bits =
      Mod32(32 - obu_sequence_header_->order_hint_bits);
  obu_frame_header_->order_hint = 17;

  const int8_t last_frame_idx = 0;
  const int8_t gold_frame_idx = 1;

  // Since all reference frames are forward references, we set the remaining
  // five references in reverse chronological order. So Last2, Last3, Backward,
  // Alternate2, and Alternate are set to 7, 6, 5, 4, and 3, respectively.

  EXPECT_TRUE(ObuSetFrameReferences(last_frame_idx, gold_frame_idx));

  EXPECT_EQ(
      obu_frame_header_
          ->reference_frame_index[kReferenceFrameLast - kReferenceFrameLast],
      0);
  EXPECT_EQ(
      obu_frame_header_
          ->reference_frame_index[kReferenceFrameLast2 - kReferenceFrameLast],
      7);
  EXPECT_EQ(
      obu_frame_header_
          ->reference_frame_index[kReferenceFrameLast3 - kReferenceFrameLast],
      6);
  EXPECT_EQ(
      obu_frame_header_
          ->reference_frame_index[kReferenceFrameGolden - kReferenceFrameLast],
      1);
  EXPECT_EQ(obu_frame_header_->reference_frame_index[kReferenceFrameBackward -
                                                     kReferenceFrameLast],
            5);
  EXPECT_EQ(obu_frame_header_->reference_frame_index[kReferenceFrameAlternate2 -
                                                     kReferenceFrameLast],
            4);
  EXPECT_EQ(obu_frame_header_->reference_frame_index[kReferenceFrameAlternate -
                                                     kReferenceFrameLast],
            3);
}

TEST_F(ObuParserTest, LoopFilterParameters) {
  LoopFilter gold;
  memset(&gold, 0, sizeof(gold));

  BytesAndBits data;
  data.AppendBit(0);  // dummy.

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->primary_reference_frame = kPrimaryReferenceNone;
  obu_frame_header_->coded_lossless = true;
  gold.ref_deltas[kReferenceFrameIntra] = 1;
  gold.ref_deltas[kReferenceFrameGolden] = -1;
  gold.ref_deltas[kReferenceFrameAlternate] = -1;
  gold.ref_deltas[kReferenceFrameAlternate2] = -1;
  ASSERT_TRUE(ObuParseLoopFilterParameters());
  VerifyLoopFilterParameters(gold);

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->primary_reference_frame = kPrimaryReferenceNone;
  obu_frame_header_->allow_intrabc = true;
  ASSERT_TRUE(ObuParseLoopFilterParameters());
  VerifyLoopFilterParameters(gold);

  gold.level[0] = 32;
  gold.level[3] = 48;
  gold.sharpness = 4;
  data.Clear();
  for (const auto& level : gold.level) {
    data.AppendLiteral(6, level);
  }
  data.AppendLiteral(3, gold.sharpness);
  data.AppendBit(0);  // delta_enabled.

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->primary_reference_frame = kPrimaryReferenceNone;
  ASSERT_TRUE(ObuParseLoopFilterParameters());
  VerifyLoopFilterParameters(gold);

  gold.delta_enabled = true;
  gold.delta_update = true;
  gold.ref_deltas[0] = 20;
  gold.mode_deltas[0] = -20;
  data.SetBit(27, 1);  // delta_enabled.
  data.AppendBit(1);   // delta_update.
  for (int i = 0; i < kNumReferenceFrameTypes; ++i) {
    if (i == 0) {
      data.AppendBit(1);  // update_ref_delta.
      data.AppendInverseSignedLiteral(6, gold.ref_deltas[0]);  // ref_delta.
    } else {
      data.AppendBit(0);  // update_ref_delta.
    }
  }
  for (int i = 0; i < kLoopFilterMaxModeDeltas; ++i) {
    if (i == 0) {
      data.AppendBit(1);  // update_mode_delta.
      data.AppendInverseSignedLiteral(6, gold.mode_deltas[0]);  // mode_delta.
    } else {
      data.AppendBit(0);  // update_mode_delta.
    }
  }

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->primary_reference_frame = kPrimaryReferenceNone;
  ASSERT_TRUE(ObuParseLoopFilterParameters());
  VerifyLoopFilterParameters(gold);
}

TEST_F(ObuParserTest, QuantizerParameters) {
  QuantizerParameters gold = {};
  gold.base_index = 48;

  BytesAndBits data;
  data.AppendLiteral(8, gold.base_index);
  data.AppendLiteral(3, 0);  // delta_coded.
  data.AppendBit(0);         // use_matrix.

  ASSERT_TRUE(Init(data.GenerateData()));
  ASSERT_TRUE(ObuParseQuantizerParameters());
  VerifyQuantizerParameters(gold);
}

TEST_F(ObuParserTest, QuantizerParametersMonochrome) {
  QuantizerParameters gold = {};
  gold.base_index = 48;

  BytesAndBits data;
  data.AppendLiteral(8, gold.base_index);
  data.AppendBit(0);  // delta_coded.
  data.AppendBit(0);  // use_matrix.
  // The quantizer parameters end here. Add a 1 bit. It should not be parsed.
  data.AppendBit(1);  // Would be segmentation_enabled in a bitstream.

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->color_config.is_monochrome = true;
  ASSERT_TRUE(ObuParseQuantizerParameters());
  VerifyQuantizerParameters(gold);
}

TEST_F(ObuParserTest, QuantizerParametersDeltaCoded) {
  QuantizerParameters gold = {};
  gold.base_index = 48;
  gold.delta_dc[kPlaneY] = -30;

  BytesAndBits data;
  data.AppendLiteral(8, gold.base_index);
  data.AppendBit(1);  // delta_coded.
  data.AppendInverseSignedLiteral(6, gold.delta_dc[kPlaneY]);
  data.AppendLiteral(2, 0);  // delta_coded u dc/ac.
  data.AppendBit(0);         // use_matrix.

  ASSERT_TRUE(Init(data.GenerateData()));
  ASSERT_TRUE(ObuParseQuantizerParameters());
  VerifyQuantizerParameters(gold);

  gold.delta_dc[kPlaneU] = -40;
  gold.delta_dc[kPlaneV] = gold.delta_dc[kPlaneU];
  data.SetBit(16, 1);  // delta_coded.
  data.InsertInverseSignedLiteral(17, 6, gold.delta_dc[kPlaneU]);

  ASSERT_TRUE(Init(data.GenerateData()));
  ASSERT_TRUE(ObuParseQuantizerParameters());
  VerifyQuantizerParameters(gold);

  gold.delta_ac[kPlaneU] = 50;
  gold.delta_ac[kPlaneV] = gold.delta_ac[kPlaneU];
  data.SetBit(24, 1);  // delta_coded.
  data.InsertInverseSignedLiteral(25, 6, gold.delta_ac[kPlaneU]);

  ASSERT_TRUE(Init(data.GenerateData()));
  ASSERT_TRUE(ObuParseQuantizerParameters());
  VerifyQuantizerParameters(gold);

  gold.delta_dc[kPlaneV] = 60;
  gold.delta_ac[kPlaneV] = 0;
  data.InsertBit(16, 1);  // diff_uv_delta.
  data.InsertBit(33, 1);  // delta_coded.
  data.InsertInverseSignedLiteral(34, 6, gold.delta_dc[kPlaneV]);
  data.InsertBit(41, 0);  // delta_coded.

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->color_config.separate_uv_delta_q = true;
  ASSERT_TRUE(ObuParseQuantizerParameters());
  VerifyQuantizerParameters(gold);

  gold.delta_ac[kPlaneV] = -20;
  data.SetBit(41, 1);  // delta_coded.
  data.InsertInverseSignedLiteral(42, 6, gold.delta_ac[kPlaneV]);

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->color_config.separate_uv_delta_q = true;
  ASSERT_TRUE(ObuParseQuantizerParameters());
  VerifyQuantizerParameters(gold);
}

TEST_F(ObuParserTest, QuantizerParametersUseQmatrix) {
  QuantizerParameters gold = {};
  gold.base_index = 48;
  gold.use_matrix = true;
  gold.matrix_level[kPlaneY] = 3;
  gold.matrix_level[kPlaneU] = 6;
  gold.matrix_level[kPlaneV] = gold.matrix_level[kPlaneU];

  // Test three cases.
  // 1. separate_uv_delta_q = false (which implies diff_uv_delta = false).
  BytesAndBits data;
  data.AppendLiteral(8, gold.base_index);
  data.AppendLiteral(3, 0);  // delta_coded.
  data.AppendBit(static_cast<uint8_t>(gold.use_matrix));
  data.AppendLiteral(4, gold.matrix_level[kPlaneY]);
  data.AppendLiteral(4, gold.matrix_level[kPlaneU]);

  ASSERT_TRUE(Init(data.GenerateData()));
  ASSERT_TRUE(ObuParseQuantizerParameters());
  VerifyQuantizerParameters(gold);

  // 2. separate_uv_delta_q = true and diff_uv_delta = false.
  gold.matrix_level[kPlaneV] = 5;
  data.InsertBit(9, 0);  // diff_uv_delta.
  data.AppendLiteral(4, gold.matrix_level[kPlaneV]);

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->color_config.separate_uv_delta_q = true;
  ASSERT_TRUE(ObuParseQuantizerParameters());
  VerifyQuantizerParameters(gold);

  // 3. separate_uv_delta_q = true and diff_uv_delta = true.
  data.SetBit(9, 1);             // diff_uv_delta.
  data.InsertLiteral(12, 2, 0);  // delta_coded.
  ASSERT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->color_config.separate_uv_delta_q = true;
  ASSERT_TRUE(ObuParseQuantizerParameters());
  VerifyQuantizerParameters(gold);
}

TEST_F(ObuParserTest, SegmentationParameters) {
  const int kPrimaryReferenceNotNone = 1;
  const int kPrevFrameIndexNotNone = 2;

  // Set up decoder_state_ with a previous frame containing saved segmentation
  // parameters.
  decoder_state_.reference_frame[kPrevFrameIndexNotNone] =
      buffer_pool_->GetFreeBuffer();
  ASSERT_NE(decoder_state_.reference_frame[kPrevFrameIndexNotNone], nullptr);
  Segmentation prev_segmentation = {};
  prev_segmentation.feature_enabled[2][0] = true;
  prev_segmentation.feature_enabled[5][0] = true;
  prev_segmentation.last_active_segment_id = 5;
  decoder_state_.reference_frame[kPrevFrameIndexNotNone]
      ->SetSegmentationParameters(prev_segmentation);

  Segmentation gold;
  memset(&gold, 0, sizeof(gold));

  BytesAndBits data;
  data.AppendBit(0);  // segmentation_enabled.

  // Since segmentation_enabled is false, we expect the parameters to be all
  // zero/false.
  ASSERT_TRUE(ParseSegmentationParameters(
      data.GenerateData(), kPrimaryReferenceNotNone, kPrevFrameIndexNotNone));
  VerifySegmentationParameters(gold);

  gold.enabled = true;
  gold.update_map = true;
  gold.temporal_update = true;
  data.SetBit(0, static_cast<uint8_t>(gold.enabled));
  data.AppendBit(static_cast<uint8_t>(gold.update_map));
  data.AppendBit(static_cast<uint8_t>(gold.temporal_update));
  data.AppendBit(static_cast<uint8_t>(gold.update_data));

  // Since update_data is false, we expect the parameters to be loaded from the
  // previous frame in |decoder_state_|. So change |gold| accordingly.
  gold.feature_enabled[2][0] = true;
  gold.feature_enabled[5][0] = true;
  gold.last_active_segment_id = 5;

  ASSERT_TRUE(ParseSegmentationParameters(
      data.GenerateData(), kPrimaryReferenceNotNone, kPrevFrameIndexNotNone));
  VerifySegmentationParameters(gold);

  OverrideSegmentation(&data, &gold, 3);

  ASSERT_TRUE(ParseSegmentationParameters(
      data.GenerateData(), kPrimaryReferenceNotNone, kPrevFrameIndexNotNone));
  VerifySegmentationParameters(gold);

  // If primary_ref_frame is kPrimaryReferenceNone, these three fields are
  // implied.
  data.RemoveBit(1);  // segmentation_update_map.
  data.RemoveBit(1);  // segmentation_temporal_update.
  data.RemoveBit(1);  // segmentation_update_data.
  gold.update_map = true;
  gold.temporal_update = false;
  gold.update_data = true;

  // Since update_data is true, we expect the parameters to be read from
  // |data|.
  ASSERT_TRUE(ParseSegmentationParameters(data.GenerateData(),
                                          kPrimaryReferenceNone, 0));
  VerifySegmentationParameters(gold);
}

TEST_F(ObuParserTest, QuantizerIndexDeltaParameters) {
  BytesAndBits data;
  data.AppendBit(1);         // delta_q_present.
  data.AppendLiteral(2, 2);  // delta_q_res.

  Delta gold;
  memset(&gold, 0, sizeof(gold));

  ASSERT_TRUE(Init(data.GenerateData()));
  ASSERT_TRUE(ObuParseQuantizerIndexDeltaParameters());
  VerifyDeltaParameters(gold, obu_->frame_header().delta_q);

  gold.present = true;
  gold.scale = 2;
  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->quantizer.base_index = 40;
  ASSERT_TRUE(ObuParseQuantizerIndexDeltaParameters());
  VerifyDeltaParameters(gold, obu_->frame_header().delta_q);
}

TEST_F(ObuParserTest, LoopFilterDeltaParameters) {
  BytesAndBits data;
  data.AppendBit(1);         // delta_lf_present.
  data.AppendLiteral(2, 2);  // delta_lf_res.
  data.AppendBit(1);         // delta_lf_multi.

  Delta gold;
  memset(&gold, 0, sizeof(gold));

  // delta_q_present is false, so loop filter delta will not be read.
  ASSERT_TRUE(Init(data.GenerateData()));
  ASSERT_TRUE(ObuParseLoopFilterDeltaParameters());
  VerifyDeltaParameters(gold, obu_->frame_header().delta_lf);

  // allow_intrabc is true, so loop filter delta will not be read.
  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->delta_q.present = true;
  obu_frame_header_->allow_intrabc = true;
  ASSERT_TRUE(ObuParseLoopFilterDeltaParameters());
  VerifyDeltaParameters(gold, obu_->frame_header().delta_lf);

  gold.present = true;
  gold.scale = 2;
  gold.multi = true;
  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->delta_q.present = true;
  ASSERT_TRUE(ObuParseLoopFilterDeltaParameters());
  VerifyDeltaParameters(gold, obu_->frame_header().delta_lf);
}

TEST_F(ObuParserTest, ComputeSegmentLosslessAndQIndex) {
  BytesAndBits data;
  data.AppendBit(0);  // dummy.

  ASSERT_TRUE(Init(data.GenerateData()));

  // Segmentation is disabled. All quantizers are 0.
  ObuComputeSegmentLosslessAndQIndex();
  EXPECT_TRUE(obu_->frame_header().coded_lossless);
  EXPECT_TRUE(obu_->frame_header().upscaled_lossless);
  for (const auto& qindex : obu_->frame_header().segmentation.qindex) {
    EXPECT_EQ(qindex, 0);
  }

  // Segmentation is enabled. All quantizers are zero.
  obu_frame_header_->segmentation.enabled = true;
  ObuComputeSegmentLosslessAndQIndex();
  EXPECT_TRUE(obu_->frame_header().coded_lossless);
  EXPECT_TRUE(obu_->frame_header().upscaled_lossless);
  for (const auto& qindex : obu_->frame_header().segmentation.qindex) {
    EXPECT_EQ(qindex, 0);
  }

  // Segmentation is enabled. All quantizers are zero. upscaled_width != width.
  obu_frame_header_->segmentation.enabled = true;
  obu_frame_header_->upscaled_width = 100;
  ObuComputeSegmentLosslessAndQIndex();
  EXPECT_TRUE(obu_->frame_header().coded_lossless);
  EXPECT_FALSE(obu_->frame_header().upscaled_lossless);
  for (const auto& qindex : obu_->frame_header().segmentation.qindex) {
    EXPECT_EQ(qindex, 0);
  }

  // Segmentation in disabled. Some quantizer deltas are non zero.
  obu_frame_header_->segmentation.enabled = false;
  obu_frame_header_->quantizer.delta_dc[kPlaneY] = 40;
  ObuComputeSegmentLosslessAndQIndex();
  EXPECT_FALSE(obu_->frame_header().coded_lossless);
  EXPECT_FALSE(obu_->frame_header().upscaled_lossless);
  for (const auto& qindex : obu_->frame_header().segmentation.qindex) {
    EXPECT_EQ(qindex, 0);
  }

  // Segmentation is disabled. Quantizer base index is non zero.
  obu_frame_header_->segmentation.enabled = true;
  obu_frame_header_->quantizer.delta_dc[kPlaneY] = 0;
  obu_frame_header_->quantizer.base_index = 40;
  ObuComputeSegmentLosslessAndQIndex();
  EXPECT_FALSE(obu_->frame_header().coded_lossless);
  EXPECT_FALSE(obu_->frame_header().upscaled_lossless);
  for (const auto& qindex : obu_->frame_header().segmentation.qindex) {
    EXPECT_EQ(qindex, 40);
  }
}

TEST_F(ObuParserTest, CdefParameters) {
  Cdef gold;
  memset(&gold, 0, sizeof(gold));
  const int coeff_shift = 2;  // bitdepth - 8.
  gold.damping = 3 + coeff_shift;

  BytesAndBits data;
  data.AppendBit(0);  // dummy.

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->color_config.bitdepth = 10;
  ASSERT_TRUE(ObuParseCdefParameters());
  // Cdef will be {0} except for damping because enable_cdef is false.
  VerifyCdefParameters(gold);

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->enable_cdef = true;
  obu_sequence_header_->color_config.bitdepth = 10;
  obu_frame_header_->coded_lossless = true;
  ASSERT_TRUE(ObuParseCdefParameters());
  // Cdef will be {0} except for damping because coded_lossless is true.
  VerifyCdefParameters(gold);

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->enable_cdef = true;
  obu_sequence_header_->color_config.bitdepth = 10;
  obu_frame_header_->allow_intrabc = true;
  ASSERT_TRUE(ObuParseCdefParameters());
  // Cdef will be {0} except for damping because allow_intrabc is true.
  VerifyCdefParameters(gold);

  gold.damping = 5;
  gold.bits = 1;
  data.Clear();
  data.AppendLiteral(2, gold.damping - 3);  // cdef_damping_minus3.
  gold.damping += coeff_shift;
  data.AppendLiteral(2, gold.bits);  // cdef_bits.
  for (int i = 0; i < 2; ++i) {
    gold.y_primary_strength[i] = 10;
    gold.y_secondary_strength[i] = (i == 0) ? 2 : 3;
    gold.uv_primary_strength[i] = 12;
    gold.uv_secondary_strength[i] = (i == 1) ? 2 : 3;
    data.AppendLiteral(4, gold.y_primary_strength[i]);
    data.AppendLiteral(2, gold.y_secondary_strength[i]);
    data.AppendLiteral(4, gold.uv_primary_strength[i]);
    data.AppendLiteral(2, gold.uv_secondary_strength[i]);
    if (gold.y_secondary_strength[i] == 3) ++gold.y_secondary_strength[i];
    if (gold.uv_secondary_strength[i] == 3) ++gold.uv_secondary_strength[i];
    gold.y_primary_strength[i] <<= coeff_shift;
    gold.uv_primary_strength[i] <<= coeff_shift;
    gold.y_secondary_strength[i] <<= coeff_shift;
    gold.uv_secondary_strength[i] <<= coeff_shift;
  }

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_sequence_header_->enable_cdef = true;
  obu_sequence_header_->color_config.bitdepth = 10;
  ASSERT_TRUE(ObuParseCdefParameters());
  VerifyCdefParameters(gold);
}

TEST_F(ObuParserTest, LoopRestorationParameters) {
  for (bool use_128x128_superblock : testing::Bool()) {
    SCOPED_TRACE("use_128x128_superblock: " +
                 std::to_string(use_128x128_superblock));
    LoopRestoration gold;
    memset(&gold, 0, sizeof(gold));

    BytesAndBits data;
    data.AppendBit(0);  // dummy.

    // enable_restoration is false. nothing will be read.
    ASSERT_TRUE(Init(data.GenerateData()));
    obu_frame_header_->allow_intrabc = true;
    obu_frame_header_->coded_lossless = true;
    ASSERT_TRUE(ObuParseLoopRestorationParameters());
    VerifyLoopRestorationParameters(gold);

    // allow_intrabc is true. nothing will be read.
    ASSERT_TRUE(Init(data.GenerateData()));
    obu_frame_header_->allow_intrabc = true;
    obu_sequence_header_->enable_restoration = true;
    ASSERT_TRUE(ObuParseLoopRestorationParameters());
    VerifyLoopRestorationParameters(gold);

    // coded_lossless is true. nothing will be read.
    ASSERT_TRUE(Init(data.GenerateData()));
    obu_frame_header_->coded_lossless = true;
    obu_sequence_header_->enable_restoration = true;
    ASSERT_TRUE(ObuParseLoopRestorationParameters());
    VerifyLoopRestorationParameters(gold);

    data.Clear();
    for (int i = 0; i < kMaxPlanes; ++i) {
      data.AppendLiteral(2, kLoopRestorationTypeNone);  // lr_type.
    }

    ASSERT_TRUE(Init(data.GenerateData()));
    obu_sequence_header_->enable_restoration = true;
    obu_sequence_header_->use_128x128_superblock = use_128x128_superblock;
    ASSERT_TRUE(ObuParseLoopRestorationParameters());
    VerifyLoopRestorationParameters(gold);

    gold.type[0] = gold.type[1] = kLoopRestorationTypeWiener;
    gold.unit_size_log2[0] = gold.unit_size_log2[1] = gold.unit_size_log2[2] =
        use_128x128_superblock ? 8 : 7;
    data.SetLiteral(0, 2, gold.type[0]);  // lr_type.
    data.SetLiteral(2, 2, gold.type[0]);  // lr_type.
    data.AppendBit(1);                    // lr_unit_shift.
    if (!use_128x128_superblock) {
      data.AppendBit(0);  // lr_unit_extra_shift.
    }

    ASSERT_TRUE(Init(data.GenerateData()));
    obu_sequence_header_->enable_restoration = true;
    obu_sequence_header_->use_128x128_superblock = use_128x128_superblock;
    ASSERT_TRUE(ObuParseLoopRestorationParameters());
    VerifyLoopRestorationParameters(gold);

    if (!use_128x128_superblock) {
      gold.unit_size_log2[0] = gold.unit_size_log2[1] = gold.unit_size_log2[2] =
          8;
      data.SetBit(7, 1);  // lr_unit_extra_shift.

      ASSERT_TRUE(Init(data.GenerateData()));
      obu_sequence_header_->enable_restoration = true;
      obu_sequence_header_->use_128x128_superblock = use_128x128_superblock;
      ASSERT_TRUE(ObuParseLoopRestorationParameters());
      VerifyLoopRestorationParameters(gold);
    }

    gold.unit_size_log2[1] = gold.unit_size_log2[2] = 7;
    data.AppendBit(1);  // lr_uv_shift.

    ASSERT_TRUE(Init(data.GenerateData()));
    obu_sequence_header_->enable_restoration = true;
    obu_sequence_header_->use_128x128_superblock = use_128x128_superblock;
    obu_sequence_header_->color_config.subsampling_x = 1;
    obu_sequence_header_->color_config.subsampling_y = 1;
    ASSERT_TRUE(ObuParseLoopRestorationParameters());
    VerifyLoopRestorationParameters(gold);
  }
}

TEST_F(ObuParserTest, TxModeSyntax) {
  BytesAndBits data;
  data.AppendBit(1);  // tx_mode_select.

  ASSERT_TRUE(Init(data.GenerateData()));
  ASSERT_TRUE(ObuParseTxModeSyntax());
  EXPECT_EQ(kTxModeSelect, obu_->frame_header().tx_mode);

  data.SetBit(0, 0);  // tx_mode_select.

  ASSERT_TRUE(Init(data.GenerateData()));
  ASSERT_TRUE(ObuParseTxModeSyntax());
  EXPECT_EQ(kTxModeLargest, obu_->frame_header().tx_mode);

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->coded_lossless = true;
  ASSERT_TRUE(ObuParseTxModeSyntax());
  EXPECT_EQ(kTxModeOnly4x4, obu_->frame_header().tx_mode);
}

TEST_F(ObuParserTest, FrameReferenceModeSyntax) {
  BytesAndBits data;
  data.AppendBit(0);  // dummy.

  ASSERT_TRUE(ParseFrameReferenceModeSyntax(data.GenerateData(), kFrameKey));
  EXPECT_FALSE(obu_->frame_header().reference_mode_select);

  data.SetBit(0, 1);  // reference_mode_select.

  ASSERT_TRUE(ParseFrameReferenceModeSyntax(data.GenerateData(), kFrameInter));
  EXPECT_TRUE(obu_->frame_header().reference_mode_select);
}

TEST_F(ObuParserTest, SkipModeParameters) {
  BytesAndBits data;
  data.AppendBit(1);  // skip_mode_present.

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->frame_type = kFrameKey;
  ASSERT_FALSE(ObuIsSkipModeAllowed());
  ASSERT_TRUE(ObuParseSkipModeParameters());
  EXPECT_FALSE(obu_->frame_header().skip_mode_present);

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->frame_type = kFrameInter;
  obu_frame_header_->reference_mode_select = true;
  ASSERT_FALSE(ObuIsSkipModeAllowed());
  ASSERT_TRUE(ObuParseSkipModeParameters());
  EXPECT_FALSE(obu_->frame_header().skip_mode_present);

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->frame_type = kFrameInter;
  obu_frame_header_->reference_mode_select = true;
  obu_sequence_header_->enable_order_hint = true;
  obu_sequence_header_->order_hint_bits = 7;
  obu_sequence_header_->order_hint_shift_bits =
      Mod32(32 - obu_sequence_header_->order_hint_bits);
  ASSERT_FALSE(ObuIsSkipModeAllowed());
  ASSERT_TRUE(ObuParseSkipModeParameters());
  EXPECT_FALSE(obu_->frame_header().skip_mode_present);

  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->frame_type = kFrameInter;
  obu_frame_header_->reference_mode_select = true;
  obu_frame_header_->order_hint = 1;
  decoder_state_.order_hint = 1;
  obu_sequence_header_->enable_order_hint = true;
  obu_sequence_header_->order_hint_bits = 7;
  obu_sequence_header_->order_hint_shift_bits =
      Mod32(32 - obu_sequence_header_->order_hint_bits);
  ASSERT_FALSE(ObuIsSkipModeAllowed());
  ASSERT_TRUE(ObuParseSkipModeParameters());
  EXPECT_FALSE(obu_->frame_header().skip_mode_present);

  ASSERT_TRUE(Init(data.GenerateData()));
  for (int i = 0; i < kNumInterReferenceFrameTypes; ++i) {
    obu_frame_header_->reference_frame_index[i] = i;
    decoder_state_.reference_order_hint[i] = i;
  }
  obu_frame_header_->frame_type = kFrameInter;
  obu_frame_header_->reference_mode_select = true;
  obu_frame_header_->order_hint = 1;
  decoder_state_.order_hint = 1;
  obu_sequence_header_->enable_order_hint = true;
  obu_sequence_header_->order_hint_bits = 7;
  obu_sequence_header_->order_hint_shift_bits =
      Mod32(32 - obu_sequence_header_->order_hint_bits);
  ASSERT_TRUE(ObuIsSkipModeAllowed());
  ASSERT_TRUE(ObuParseSkipModeParameters());
  EXPECT_TRUE(obu_->frame_header().skip_mode_present);
}

TEST_F(ObuParserTest, AllowWarpedMotion) {
  BytesAndBits data;
  data.AppendBit(0xff);  // dummy.

  // IsIntraFrame is true, so nothing will be read.
  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->frame_type = kFrameKey;
  obu_frame_header_->error_resilient_mode = false;
  obu_sequence_header_->enable_warped_motion = true;
  ASSERT_TRUE(ObuReadAllowWarpedMotion());
  EXPECT_FALSE(obu_->frame_header().allow_warped_motion);

  // error_resilient_mode is true, so nothing will be read.
  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->frame_type = kFrameInter;
  obu_frame_header_->error_resilient_mode = true;
  obu_sequence_header_->enable_warped_motion = true;
  ASSERT_TRUE(ObuReadAllowWarpedMotion());
  EXPECT_FALSE(obu_->frame_header().allow_warped_motion);

  // enable_warped_motion is false, so nothing will be read.
  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->frame_type = kFrameInter;
  obu_frame_header_->error_resilient_mode = false;
  obu_sequence_header_->enable_warped_motion = false;
  ASSERT_TRUE(ObuReadAllowWarpedMotion());
  EXPECT_FALSE(obu_->frame_header().allow_warped_motion);

  // allow_warped_motion will be read and equal to true.
  ASSERT_TRUE(Init(data.GenerateData()));
  obu_frame_header_->frame_type = kFrameInter;
  obu_frame_header_->error_resilient_mode = false;
  obu_sequence_header_->enable_warped_motion = true;
  ASSERT_TRUE(ObuReadAllowWarpedMotion());
  EXPECT_TRUE(obu_->frame_header().allow_warped_motion);
}

TEST_F(ObuParserTest, GlobalMotionParameters) {
  BytesAndBits data;
  data.AppendBit(0);  // dummy.
  std::array<GlobalMotion, kNumReferenceFrameTypes> gold;
  for (int i = kReferenceFrameLast; i <= kReferenceFrameAlternate; ++i) {
    gold[i].type = kGlobalMotionTransformationTypeIdentity;
    for (int j = 0; j < 6; ++j) {
      gold[i].params[j] = (j % 3 == 2) ? 1 << kWarpedModelPrecisionBits : 0;
    }
  }

  ASSERT_TRUE(ParseGlobalMotionParameters(data.GenerateData(), kFrameKey));
  VerifyGlobalMotionParameters(gold);

  data.Clear();
  for (int i = kReferenceFrameLast; i <= kReferenceFrameAlternate; ++i) {
    // is_global=1; is_rot_zoom=1; parameter_values;
    data.AppendBytes(kDefaultGlobalMotionParametersRotZoom);

    // Magic numbers based on kDefaultGlobalMotionParametersRotZoom.
    gold[i].type = kGlobalMotionTransformationTypeRotZoom;
    gold[i].params[0] = -73728;
    gold[i].params[1] = -23552;
    gold[i].params[2] = 65952;
    gold[i].params[3] = -62;
    gold[i].params[4] = 62;
    gold[i].params[5] = 65952;
  }

  ASSERT_TRUE(ParseGlobalMotionParameters(data.GenerateData(), kFrameInter));
  VerifyGlobalMotionParameters(gold);

  data.Clear();
  for (int i = kReferenceFrameLast; i <= kReferenceFrameAlternate; ++i) {
    // This bit is not part of the hex string because it would make the whole
    // string not align to 8 bits. Appending this separately so that we can keep
    // the rest of them a magic hex string.
    data.AppendBit(1);  // is_global.
    // is_rot_zoom=0; is_translation=0; parameter_values;
    data.AppendBytes(kDefaultGlobalMotionParametersAffine);

    // Magic numbers based on kDefaultGlobalMotionParametersAffine.
    gold[i].type = kGlobalMotionTransformationTypeAffine;
    gold[i].params[4] = -62;
  }

  ASSERT_TRUE(ParseGlobalMotionParameters(data.GenerateData(), kFrameInter));
  VerifyGlobalMotionParameters(gold);
}

TEST_F(ObuParserTest, FilmGrainParameters) {
  BytesAndBits data;
  data.AppendBit(0);  // dummy.

  // Test film grain not present.
  FilmGrainParams gold = {};
  ObuSequenceHeader sequence_header = {};
  sequence_header.film_grain_params_present = false;
  ObuFrameHeader frame_header = {};
  ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header,
                                       frame_header));
  VerifyFilmGrainParameters(gold);

  // Test if show_frame = false and showable_frame = false.
  data.Clear();
  gold = {};
  sequence_header.film_grain_params_present = true;
  frame_header.show_frame = false;
  frame_header.showable_frame = false;
  ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header,
                                       frame_header));
  VerifyFilmGrainParameters(gold);

  // Test if apply_grain = false.
  data.Clear();
  gold = {};
  sequence_header.film_grain_params_present = true;
  frame_header.show_frame = true;
  frame_header.showable_frame = true;
  data.AppendBit(0);
  ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header,
                                       frame_header));
  VerifyFilmGrainParameters(gold);

  // Test if update_grain = false.
  data.Clear();
  gold = {};
  sequence_header.film_grain_params_present = true;
  frame_header.show_frame = true;
  frame_header.showable_frame = true;
  frame_header.frame_type = kFrameInter;
  for (auto& index : frame_header.reference_frame_index) {
    index = 1;
  }
  data.AppendBit(1);
  gold.apply_grain = true;
  data.AppendLiteral(16, 8);
  gold.grain_seed = 8;
  data.AppendBit(0);
  gold.update_grain = false;
  data.AppendLiteral(3, 1);
  gold.reference_index = 1;
  // Set up decoder_state_ with a previous frame containing saved film grain
  // parameters.
  decoder_state_.reference_frame[1] = buffer_pool_->GetFreeBuffer();
  EXPECT_NE(decoder_state_.reference_frame[1], nullptr);
  FilmGrainParams prev_grain_params = {};
  prev_grain_params.apply_grain = true;
  prev_grain_params.grain_seed = 11;
  prev_grain_params.update_grain = true;
  decoder_state_.reference_frame[1]->set_film_grain_params(prev_grain_params);
  ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header,
                                       frame_header));
  VerifyFilmGrainParameters(gold);

  // Test if update_grain = true, is_monochrome = true;
  data.Clear();
  gold = {};
  frame_header.frame_type = kFrameKey;
  for (auto& index : frame_header.reference_frame_index) {
    index = 0;
  }
  data.AppendBit(1);
  gold.apply_grain = true;
  data.AppendLiteral(16, 8);
  gold.grain_seed = 8;
  gold.update_grain = true;
  data.AppendLiteral(4, 10);
  gold.num_y_points = 10;
  for (int i = 0; i < gold.num_y_points; ++i) {
    data.AppendLiteral(8, 2 * i);
    gold.point_y_value[i] = 2 * i;
    data.AppendLiteral(8, i);
    gold.point_y_scaling[i] = i;
  }
  sequence_header.color_config.is_monochrome = true;
  gold.chroma_scaling_from_luma = false;
  gold.num_u_points = 0;
  gold.num_v_points = 0;
  data.AppendLiteral(2, 3);
  gold.chroma_scaling = 11;
  data.AppendLiteral(2, 1);
  gold.auto_regression_coeff_lag = 1;
  const int num_pos_luma =
      2 * gold.auto_regression_coeff_lag * (gold.auto_regression_coeff_lag + 1);
  for (int i = 0; i < num_pos_luma; ++i) {
    data.AppendLiteral(8, i + 128);
    gold.auto_regression_coeff_y[i] = i;
  }
  data.AppendLiteral(2, 0);
  gold.auto_regression_shift = 6;
  data.AppendLiteral(2, 1);
  gold.grain_scale_shift = 1;
  data.AppendBit(1);
  gold.overlap_flag = true;
  data.AppendBit(0);
  gold.clip_to_restricted_range = false;
  ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header,
                                       frame_header));
  ASSERT_TRUE(
      obu_->frame_header().frame_type == kFrameInter ||
      obu_->frame_header().film_grain_params.update_grain);  // a implies b.
  VerifyFilmGrainParameters(gold);

  // Test if update_grain = true, is_monochrome = false;
  data.Clear();
  gold = {};
  frame_header.frame_type = kFrameKey;
  data.AppendBit(1);
  gold.apply_grain = true;
  data.AppendLiteral(16, 8);
  gold.grain_seed = 8;
  gold.update_grain = true;
  data.AppendLiteral(4, 10);
  gold.num_y_points = 10;
  for (int i = 0; i < gold.num_y_points; ++i) {
    data.AppendLiteral(8, 2 * i);
    gold.point_y_value[i] = 2 * i;
    data.AppendLiteral(8, i);
    gold.point_y_scaling[i] = i;
  }
  sequence_header.color_config.is_monochrome = false;
  data.AppendBit(0);
  gold.chroma_scaling_from_luma = false;
  data.AppendLiteral(4, 5);
  gold.num_u_points = 5;
  for (int i = 0; i < gold.num_u_points; ++i) {
    data.AppendLiteral(8, 2 * i + 1);
    gold.point_u_value[i] = 2 * i + 1;
    data.AppendLiteral(8, i);
    gold.point_u_scaling[i] = i;
  }
  data.AppendLiteral(4, 3);
  gold.num_v_points = 3;
  for (int i = 0; i < gold.num_v_points; ++i) {
    data.AppendLiteral(8, i);
    gold.point_v_value[i] = i;
    data.AppendLiteral(8, i + 1);
    gold.point_v_scaling[i] = i + 1;
  }
  data.AppendLiteral(2, 3);
  gold.chroma_scaling = 11;
  data.AppendLiteral(2, 1);
  gold.auto_regression_coeff_lag = 1;
  const int num_pos_luma2 =
      2 * gold.auto_regression_coeff_lag * (gold.auto_regression_coeff_lag + 1);
  for (int i = 0; i < num_pos_luma2; ++i) {
    data.AppendLiteral(8, i + 128);
    gold.auto_regression_coeff_y[i] = i;
  }
  for (int i = 0; i < num_pos_luma2 + 1; ++i) {
    data.AppendLiteral(8, i);
    gold.auto_regression_coeff_u[i] = i - 128;
  }
  for (int i = 0; i < num_pos_luma2 + 1; ++i) {
    data.AppendLiteral(8, i);
    gold.auto_regression_coeff_v[i] = i - 128;
  }
  data.AppendLiteral(2, 0);
  gold.auto_regression_shift = 6;
  data.AppendLiteral(2, 1);
  gold.grain_scale_shift = 1;
  data.AppendLiteral(8, 2);
  gold.u_multiplier = -126;
  data.AppendLiteral(8, 1);
  gold.u_luma_multiplier = -127;
  data.AppendLiteral(9, 3);
  gold.u_offset = -253;
  data.AppendLiteral(8, 3);
  gold.v_multiplier = -125;
  data.AppendLiteral(8, 2);
  gold.v_luma_multiplier = -126;
  data.AppendLiteral(9, 1);
  gold.v_offset = -255;
  data.AppendBit(1);
  gold.overlap_flag = true;
  data.AppendBit(0);
  gold.clip_to_restricted_range = false;
  ASSERT_TRUE(ParseFilmGrainParameters(data.GenerateData(), sequence_header,
                                       frame_header));
  ASSERT_TRUE(
      obu_->frame_header().frame_type == kFrameInter ||
      obu_->frame_header().film_grain_params.update_grain);  // a implies b.
  VerifyFilmGrainParameters(gold);
}

TEST_F(ObuParserTest, TileInfoSyntax) {
  BytesAndBits data;
  TileInfo gold;
  memset(&gold, 0, sizeof(gold));

  gold.uniform_spacing = true;
  gold.tile_columns_log2 = 1;
  gold.tile_columns = 2;
  gold.tile_rows_log2 = 1;
  gold.tile_rows = 2;
  gold.tile_count = 4;
  gold.tile_column_start[1] = 64;
  gold.tile_column_start[2] = 88;
  gold.tile_row_start[1] = 64;
  gold.tile_row_start[2] = 72;
  gold.context_update_id = 3;
  gold.tile_size_bytes = 4;
  data.AppendBit(static_cast<uint8_t>(gold.uniform_spacing));
  data.AppendBit(1);  // increment_tile_cols_log2.
  data.AppendBit(0);  // increment_tile_cols_log2.
  data.AppendBit(1);  // increment_tile_rows_log2.
  data.AppendBit(0);  // increment_tile_rows_log2.
  data.AppendBit(1);  // context update id, columns_log2+rows_log2 bits
  data.AppendBit(1);
  data.AppendLiteral(2, gold.tile_size_bytes - 1);

  ASSERT_TRUE(ParseTileInfoSyntax(data.GenerateData(), 88, 72, true));
  VerifyTileInfoParameters(gold);

  gold.uniform_spacing = false;
  gold.tile_column_width_in_superblocks[0] = 2;
  gold.tile_column_width_in_superblocks[1] = 1;
  gold.tile_row_height_in_superblocks[0] = 2;
  gold.tile_row_height_in_superblocks[1] = 1;

  data.SetBit(0, static_cast<uint8_t>(gold.uniform_spacing));
  // The next 4 bits remain the same except now they represent f(w - 1) and
  // extra_bit in DecodeUniform. All the subsequent bits are unchanged the
  // represent the same thing as above.

  ASSERT_TRUE(ParseTileInfoSyntax(data.GenerateData(), 88, 72, true));
  VerifyTileInfoParameters(gold);

  // No tiles.
  memset(&gold, 0, sizeof(gold));
  gold.uniform_spacing = true;
  gold.tile_columns = 1;
  gold.tile_rows = 1;
  gold.tile_count = 1;
  gold.tile_column_start[1] = 88;
  gold.tile_row_start[1] = 72;
  data.Clear();
  data.AppendBit(static_cast<uint8_t>(gold.uniform_spacing));
  data.AppendBit(0);  // tile_cols_log2.
  data.AppendBit(0);  // tile_rows_log2.

  ASSERT_TRUE(ParseTileInfoSyntax(data.GenerateData(), 88, 72, true));
  VerifyTileInfoParameters(gold);

  // 64x64 superblocks. No tiles.
  gold.tile_column_start[1] = 640;
  gold.tile_row_start[1] = 360;

  ASSERT_TRUE(ParseTileInfoSyntax(data.GenerateData(), 640, 360, false));
  VerifyTileInfoParameters(gold);
}

TEST_F(ObuParserTest, MetadataUnknownType) {
  BytesAndBits data;
  // The metadata_type 10 is a user private value (6-31).
  data.AppendLiteral(8, 10);  // metadata_type.
  // The Note in Section 5.8.1 says "Decoders should ignore the entire OBU if
  // they do not understand the metadata_type."
  ASSERT_TRUE(ParseMetadata(data.GenerateData()));
}

TEST_F(ObuParserTest, MetadataHdrCll) {
  BytesAndBits data;
  ObuMetadataHdrCll gold;
  gold.max_cll = 25;
  gold.max_fall = 100;

  data.AppendLiteral(8, kMetadataTypeHdrContentLightLevel);
  data.AppendLiteral(16, gold.max_cll);
  data.AppendLiteral(16, gold.max_fall);

  ASSERT_TRUE(ParseMetadata(data.GenerateData()));
  VerifyMetadataHdrCll(gold);
}

TEST_F(ObuParserTest, MetadataHdrMdcv) {
  BytesAndBits data;
  ObuMetadataHdrMdcv gold;
  for (int i = 0; i < 3; ++i) {
    gold.primary_chromaticity_x[i] = 0;
    gold.primary_chromaticity_y[i] = 0;
  }
  gold.white_point_chromaticity_x = 250;
  gold.white_point_chromaticity_y = 2500;
  gold.luminance_max = 6000;
  gold.luminance_min = 3000;

  data.AppendLiteral(8, kMetadataTypeHdrMasteringDisplayColorVolume);
  for (int i = 0; i < 3; ++i) {
    data.AppendLiteral(16, gold.primary_chromaticity_x[i]);
    data.AppendLiteral(16, gold.primary_chromaticity_y[i]);
  }
  data.AppendLiteral(16, gold.white_point_chromaticity_x);
  data.AppendLiteral(16, gold.white_point_chromaticity_y);
  data.AppendLiteral(32, gold.luminance_max);
  data.AppendLiteral(32, gold.luminance_min);

  ASSERT_TRUE(ParseMetadata(data.GenerateData()));
  VerifyMetadataHdrMdcv(gold);
}

TEST_F(ObuParserTest, MetadataScalability) {
  BytesAndBits data;

  data.AppendLiteral(8, kMetadataTypeScalability);
  data.AppendLiteral(8, 0);  // scalability_mode_idc

  ASSERT_TRUE(ParseMetadata(data.GenerateData()));
}

TEST_F(ObuParserTest, MetadataItutT35) {
  BytesAndBits data;
  ObuMetadataItutT35 gold;
  gold.country_code = 0xA6;  // 1 0 1 0 0 1 1 0 Switzerland
  DynamicBuffer<uint8_t> payload_bytes;
  ASSERT_TRUE(payload_bytes.Resize(10));
  gold.payload_bytes = payload_bytes.get();
  for (int i = 0; i < 10; ++i) {
    gold.payload_bytes[i] = 9 - i;
  }
  gold.payload_size = 10;

  data.AppendLiteral(8, kMetadataTypeItutT35);
  data.AppendLiteral(8, gold.country_code);
  for (int i = 0; i < 10; ++i) {
    data.AppendLiteral(8, 9 - i);
  }
  // For the kMetadataTypeItutT35 metadata type, we must include the trailing
  // bit so that the end of the itu_t_t35_payload_bytes can be identified.
  data.AppendLiteral(8, 0x80);
  data.AppendLiteral(8, 0x00);
  data.AppendLiteral(8, 0x00);

  ASSERT_TRUE(ParseMetadata(data.GenerateData()));
  VerifyMetadataItutT35(gold);

  gold.country_code = 0xFF;
  gold.country_code_extension_byte = 10;

  data.SetLiteral(8, 8, gold.country_code);
  data.InsertLiteral(16, 8, gold.country_code_extension_byte);

  ASSERT_TRUE(ParseMetadata(data.GenerateData()));
  VerifyMetadataItutT35(gold);
}

TEST_F(ObuParserTest, MetadataTimecode) {
  BytesAndBits data;

  data.AppendLiteral(8, kMetadataTypeTimecode);
  data.AppendLiteral(5, 0);   // counting_type
  data.AppendBit(1);          // full_timestamp_flag
  data.AppendBit(0);          // discontinuity_flag
  data.AppendBit(0);          // cnt_dropped_flag
  data.AppendLiteral(9, 8);   // n_frames
  data.AppendLiteral(6, 59);  // seconds_value
  data.AppendLiteral(6, 59);  // minutes_value
  data.AppendLiteral(5, 23);  // hours_value
  data.AppendLiteral(5, 0);   // time_offset_length

  ASSERT_TRUE(ParseMetadata(data.GenerateData()));
}

TEST_F(ObuParserTest, MetadataTimecodeInvalidSecondsValue) {
  BytesAndBits data;

  data.AppendLiteral(8, kMetadataTypeTimecode);
  data.AppendLiteral(5, 0);   // counting_type
  data.AppendBit(1);          // full_timestamp_flag
  data.AppendBit(0);          // discontinuity_flag
  data.AppendBit(0);          // cnt_dropped_flag
  data.AppendLiteral(9, 8);   // n_frames
  data.AppendLiteral(6, 60);  // seconds_value
  data.AppendLiteral(6, 59);  // minutes_value
  data.AppendLiteral(5, 23);  // hours_value
  data.AppendLiteral(5, 0);   // time_offset_length

  EXPECT_FALSE(ParseMetadata(data.GenerateData()));
}

TEST_F(ObuParserTest, MetadataTimecodeInvalidMinutesValue) {
  BytesAndBits data;

  data.AppendLiteral(8, kMetadataTypeTimecode);
  data.AppendLiteral(5, 0);   // counting_type
  data.AppendBit(1);          // full_timestamp_flag
  data.AppendBit(0);          // discontinuity_flag
  data.AppendBit(0);          // cnt_dropped_flag
  data.AppendLiteral(9, 8);   // n_frames
  data.AppendLiteral(6, 59);  // seconds_value
  data.AppendLiteral(6, 60);  // minutes_value
  data.AppendLiteral(5, 23);  // hours_value
  data.AppendLiteral(5, 0);   // time_offset_length

  EXPECT_FALSE(ParseMetadata(data.GenerateData()));
}

TEST_F(ObuParserTest, MetadataTimecodeInvalidHoursValue) {
  BytesAndBits data;

  data.AppendLiteral(8, kMetadataTypeTimecode);
  data.AppendLiteral(5, 0);   // counting_type
  data.AppendBit(1);          // full_timestamp_flag
  data.AppendBit(0);          // discontinuity_flag
  data.AppendBit(0);          // cnt_dropped_flag
  data.AppendLiteral(9, 8);   // n_frames
  data.AppendLiteral(6, 59);  // seconds_value
  data.AppendLiteral(6, 59);  // minutes_value
  data.AppendLiteral(5, 24);  // hours_value
  data.AppendLiteral(5, 0);   // time_offset_length

  EXPECT_FALSE(ParseMetadata(data.GenerateData()));
}

}  // namespace libgav1
