// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef QUICHE_HTTP2_TEST_TOOLS_HPACK_BLOCK_COLLECTOR_H_
#define QUICHE_HTTP2_TEST_TOOLS_HPACK_BLOCK_COLLECTOR_H_

// HpackBlockCollector implements HpackEntryDecoderListener in order to record
// the calls using HpackEntryCollector instances (one per HPACK entry). This
// supports testing of HpackBlockDecoder, which decodes entire HPACK blocks.
//
// In addition to implementing the callback methods, HpackBlockCollector also
// supports comparing two HpackBlockCollector instances (i.e. an expected and
// an actual), or a sole HPACK entry against an expected value.

#include <stddef.h>

#include <string>
#include <vector>

#include "absl/strings/string_view.h"
#include "quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h"
#include "quiche/http2/hpack/http2_hpack_constants.h"
#include "quiche/http2/test_tools/hpack_block_builder.h"
#include "quiche/http2/test_tools/hpack_entry_collector.h"
#include "quiche/http2/test_tools/http2_random.h"
#include "quiche/common/platform/api/quiche_export.h"

namespace http2 {
namespace test {

class QUICHE_NO_EXPORT HpackBlockCollector : public HpackEntryDecoderListener {
 public:
  HpackBlockCollector();
  HpackBlockCollector(const HpackBlockCollector& other);
  ~HpackBlockCollector() override;

  // Implementations of HpackEntryDecoderListener, forwarding to pending_entry_,
  // an HpackEntryCollector for the "in-progress" HPACK entry. OnIndexedHeader
  // and OnDynamicTableSizeUpdate are pending only for that one call, while
  // OnStartLiteralHeader is followed by many calls, ending with OnValueEnd.
  // Once all the calls for one HPACK entry have been received, PushPendingEntry
  // is used to append the pending_entry_ entry to the collected entries_.
  void OnIndexedHeader(size_t index) override;
  void OnDynamicTableSizeUpdate(size_t size) override;
  void OnStartLiteralHeader(HpackEntryType header_type,
                            size_t maybe_name_index) override;
  void OnNameStart(bool huffman_encoded, size_t len) override;
  void OnNameData(const char* data, size_t len) override;
  void OnNameEnd() override;
  void OnValueStart(bool huffman_encoded, size_t len) override;
  void OnValueData(const char* data, size_t len) override;
  void OnValueEnd() override;

  // Methods for creating a set of expectations (i.e. HPACK entries to compare
  // against those collected by another instance of HpackBlockCollector).

  // Add an HPACK entry for an indexed header.
  void ExpectIndexedHeader(size_t index);

  // Add an HPACK entry for a dynamic table size update.
  void ExpectDynamicTableSizeUpdate(size_t size);

  // Add an HPACK entry for a header entry with an index for the name, and a
  // literal value.
  void ExpectNameIndexAndLiteralValue(HpackEntryType type, size_t index,
                                      bool value_huffman,
                                      const std::string& value);

  // Add an HPACK entry for a header entry with a literal name and value.
  void ExpectLiteralNameAndValue(HpackEntryType type, bool name_huffman,
                                 const std::string& name, bool value_huffman,
                                 const std::string& value);

  // Shuffle the entries, in support of generating an HPACK block of entries
  // in some random order.
  void ShuffleEntries(Http2Random* rng);

  // Serialize entries_ to the HpackBlockBuilder.
  void AppendToHpackBlockBuilder(HpackBlockBuilder* hbb) const;

  // Return AssertionSuccess if there is just one entry, and it is an
  // Indexed Header with the specified index.
  ::testing::AssertionResult ValidateSoleIndexedHeader(size_t ndx) const;

  // Return AssertionSuccess if there is just one entry, and it is a
  // Dynamic Table Size Update with the specified size.
  ::testing::AssertionResult ValidateSoleDynamicTableSizeUpdate(
      size_t size) const;

  // Return AssertionSuccess if there is just one entry, and it is a Header
  // entry with an index for the name and a literal value.
  ::testing::AssertionResult ValidateSoleLiteralValueHeader(
      HpackEntryType expected_type, size_t expected_index,
      bool expected_value_huffman, absl::string_view expected_value) const;

  // Return AssertionSuccess if there is just one entry, and it is a Header
  // with a literal name and literal value.
  ::testing::AssertionResult ValidateSoleLiteralNameValueHeader(
      HpackEntryType expected_type, bool expected_name_huffman,
      absl::string_view expected_name, bool expected_value_huffman,
      absl::string_view expected_value) const;

  bool IsNotPending() const { return pending_entry_.IsClear(); }
  bool IsClear() const { return IsNotPending() && entries_.empty(); }
  void Clear();

  ::testing::AssertionResult VerifyEq(const HpackBlockCollector& that) const;

 private:
  // Push the value of pending_entry_ onto entries_, and clear pending_entry_.
  // The pending_entry_ must be complete.
  void PushPendingEntry();

  HpackEntryCollector pending_entry_;
  std::vector<HpackEntryCollector> entries_;
};

}  // namespace test
}  // namespace http2

#endif  // QUICHE_HTTP2_TEST_TOOLS_HPACK_BLOCK_COLLECTOR_H_
