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

#include "content/browser/indexed_db/indexed_db_leveldb_coding.h"

#include <stddef.h>
#include <stdint.h>

#include <array>
#include <limits>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "base/base64.h"
#include "base/compiler_specific.h"
#include "base/containers/span.h"
#include "base/test/insecure_random_generator.h"
#include "components/services/storage/indexed_db/scopes/varint_coding.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/fuzztest/src/fuzztest/fuzztest.h"

using blink::IndexedDBKey;
using blink::IndexedDBKeyPath;

namespace content::indexed_db {
namespace {

static IndexedDBKey CreateArrayIDBKey() {
  return IndexedDBKey(IndexedDBKey::KeyArray());
}

static IndexedDBKey CreateArrayIDBKey(const IndexedDBKey& key1) {
  IndexedDBKey::KeyArray array;
  array.emplace_back(key1.Clone());
  return IndexedDBKey(std::move(array));
}

static IndexedDBKey CreateArrayIDBKey(const IndexedDBKey& key1,
                                      const IndexedDBKey& key2) {
  IndexedDBKey::KeyArray array;
  array.emplace_back(key1.Clone());
  array.emplace_back(key2.Clone());
  return IndexedDBKey(std::move(array));
}

static std::string WrappedEncodeByte(char value) {
  std::string buffer;
  EncodeByte(value, &buffer);
  return buffer;
}

}  // namespace

TEST(IndexedDBLevelDBCodingTest, EncodeByte) {
  std::string expected;
  expected.push_back(0);
  unsigned char c;

  c = 0;
  expected[0] = c;
  EXPECT_EQ(expected, WrappedEncodeByte(c));

  c = 1;
  expected[0] = c;
  EXPECT_EQ(expected, WrappedEncodeByte(c));

  c = 255;
  expected[0] = c;
  EXPECT_EQ(expected, WrappedEncodeByte(c));
}

TEST(IndexedDBLevelDBCodingTest, DecodeByte) {
  std::vector<unsigned char> test_cases = {0, 1, 255};

  for (size_t i = 0; i < test_cases.size(); ++i) {
    unsigned char n = test_cases[i];
    std::string v;
    EncodeByte(n, &v);

    unsigned char res;
    ASSERT_GT(v.size(), 0u);
    std::string_view slice(v);
    EXPECT_TRUE(DecodeByte(&slice, &res));
    EXPECT_EQ(n, res);
    EXPECT_TRUE(slice.empty());
  }

  {
    std::string_view slice;
    unsigned char value;
    EXPECT_FALSE(DecodeByte(&slice, &value));
  }
}

static std::string WrappedEncodeBool(bool value) {
  std::string buffer;
  EncodeBool(value, &buffer);
  return buffer;
}

TEST(IndexedDBLevelDBCodingTest, EncodeBool) {
  {
    std::string expected;
    expected.push_back(1);
    EXPECT_EQ(expected, WrappedEncodeBool(true));
  }
  {
    std::string expected;
    expected.push_back(0);
    EXPECT_EQ(expected, WrappedEncodeBool(false));
  }
}

static int CompareKeys(const std::string& a, const std::string& b) {
  DCHECK(!a.empty());
  DCHECK(!b.empty());

  std::string_view slice_a(a);
  std::string_view slice_b(b);
  bool ok;
  int result = CompareEncodedIDBKeys(&slice_a, &slice_b, &ok);
  EXPECT_TRUE(ok);
  return result;
}

TEST(IndexedDBLevelDBCodingTest, MaxIDBKey) {
  std::string max_key = MaxIDBKey();

  std::string min_key = MinIDBKey();
  std::string array_key;
  EncodeIDBKey(IndexedDBKey(IndexedDBKey::KeyArray()), &array_key);
  std::string binary_key;
  EncodeIDBKey(IndexedDBKey(std::string("\x00\x01\x02")), &binary_key);
  std::string string_key;
  EncodeIDBKey(IndexedDBKey(u"Hello world"), &string_key);
  std::string number_key;
  EncodeIDBKey(IndexedDBKey(3.14, blink::mojom::IDBKeyType::Number),
               &number_key);
  std::string date_key;
  EncodeIDBKey(IndexedDBKey(1000000, blink::mojom::IDBKeyType::Date),
               &date_key);

  EXPECT_GT(CompareKeys(max_key, min_key), 0);
  EXPECT_GT(CompareKeys(max_key, array_key), 0);
  EXPECT_GT(CompareKeys(max_key, binary_key), 0);
  EXPECT_GT(CompareKeys(max_key, string_key), 0);
  EXPECT_GT(CompareKeys(max_key, number_key), 0);
  EXPECT_GT(CompareKeys(max_key, date_key), 0);
}

TEST(IndexedDBLevelDBCodingTest, MinIDBKey) {
  std::string min_key = MinIDBKey();

  std::string max_key = MaxIDBKey();
  std::string array_key;
  EncodeIDBKey(IndexedDBKey(IndexedDBKey::KeyArray()), &array_key);
  std::string binary_key;
  EncodeIDBKey(IndexedDBKey(std::string("\x00\x01\x02")), &binary_key);
  std::string string_key;
  EncodeIDBKey(IndexedDBKey(u"Hello world"), &string_key);
  std::string number_key;
  EncodeIDBKey(IndexedDBKey(3.14, blink::mojom::IDBKeyType::Number),
               &number_key);
  std::string date_key;
  EncodeIDBKey(IndexedDBKey(1000000, blink::mojom::IDBKeyType::Date),
               &date_key);

  EXPECT_LT(CompareKeys(min_key, max_key), 0);
  EXPECT_LT(CompareKeys(min_key, array_key), 0);
  EXPECT_LT(CompareKeys(min_key, binary_key), 0);
  EXPECT_LT(CompareKeys(min_key, string_key), 0);
  EXPECT_LT(CompareKeys(min_key, number_key), 0);
  EXPECT_LT(CompareKeys(min_key, date_key), 0);
}

static std::string WrappedEncodeInt(int64_t value) {
  std::string buffer;
  EncodeInt(value, &buffer);
  return buffer;
}

TEST(IndexedDBLevelDBCodingTest, EncodeInt) {
  EXPECT_EQ(1u, WrappedEncodeInt(0).size());
  EXPECT_EQ(1u, WrappedEncodeInt(1).size());
  EXPECT_EQ(1u, WrappedEncodeInt(255).size());
  EXPECT_EQ(2u, WrappedEncodeInt(256).size());
  EXPECT_EQ(4u, WrappedEncodeInt(0xffffffff).size());
  EXPECT_EQ(8u, WrappedEncodeInt(-1).size());
}

TEST(IndexedDBLevelDBCodingTest, DecodeBool) {
  {
    std::string encoded;
    encoded.push_back(1);
    std::string_view slice(encoded);
    bool value;
    EXPECT_TRUE(DecodeBool(&slice, &value));
    EXPECT_TRUE(value);
    EXPECT_TRUE(slice.empty());
  }
  {
    std::string encoded;
    encoded.push_back(0);
    std::string_view slice(encoded);
    bool value;
    EXPECT_TRUE(DecodeBool(&slice, &value));
    EXPECT_FALSE(value);
    EXPECT_TRUE(slice.empty());
  }
  {
    std::string_view slice;
    bool value;
    EXPECT_FALSE(DecodeBool(&slice, &value));
  }
}

TEST(IndexedDBLevelDBCodingTest, DecodeInt) {
  std::vector<int64_t> test_cases = {
      0,
      1,
      255,
      256,
      65535,
      655536,
      7711192431755665792ll,
      0x7fffffffffffffffll,
      -3,
  };

  for (size_t i = 0; i < test_cases.size(); ++i) {
    int64_t n = test_cases[i];
    std::string v = WrappedEncodeInt(n);
    ASSERT_GT(v.size(), 0u);
    std::string_view slice(v);
    int64_t value;
    EXPECT_TRUE(DecodeInt(&slice, &value));
    EXPECT_EQ(n, value);
    EXPECT_TRUE(slice.empty());

    // Verify decoding at an offset, to detect unaligned memory access.
    v.insert(v.begin(), 1u, static_cast<char>(0));
    slice = std::string_view(v).substr(1u);
    EXPECT_TRUE(DecodeInt(&slice, &value));
    EXPECT_EQ(n, value);
    EXPECT_TRUE(slice.empty());
  }
  {
    std::string_view slice;
    int64_t value;
    EXPECT_FALSE(DecodeInt(&slice, &value));
  }
}

static std::string WrappedEncodeString(std::u16string value) {
  std::string buffer;
  EncodeString(value, &buffer);
  return buffer;
}

TEST(IndexedDBLevelDBCodingTest, EncodeString) {
  const char16_t test_string_a[] = {'f', 'o', 'o', '\0'};
  const char16_t test_string_b[] = {0xdead, 0xbeef, '\0'};

  EXPECT_EQ(0u, WrappedEncodeString(u"").size());
  EXPECT_EQ(2u, WrappedEncodeString(u"a").size());
  EXPECT_EQ(6u, WrappedEncodeString(u"foo").size());
  EXPECT_EQ(6u, WrappedEncodeString(std::u16string(test_string_a)).size());
  EXPECT_EQ(4u, WrappedEncodeString(std::u16string(test_string_b)).size());
}

TEST(IndexedDBLevelDBCodingTest, DecodeString) {
  const char16_t test_string_a[] = {'f', 'o', 'o', '\0'};
  const char16_t test_string_b[] = {0xdead, 0xbeef, '\0'};

  std::vector<std::u16string> test_cases = {u"", u"a", u"foo", test_string_a,
                                            test_string_b};

  for (size_t i = 0; i < test_cases.size(); ++i) {
    const std::u16string& test_case = test_cases[i];
    std::string v = WrappedEncodeString(test_case);

    std::string_view slice;
    if (v.size()) {
      slice = std::string_view(&*v.begin(), v.size());
    }

    std::u16string result;
    EXPECT_TRUE(DecodeString(&slice, &result));
    EXPECT_EQ(test_case, result);
    EXPECT_TRUE(slice.empty());

    // Verify decoding at an offset, to detect unaligned memory access.
    v.insert(v.begin(), 1u, static_cast<char>(0));
    slice = std::string_view(v).substr(1u);
    EXPECT_TRUE(DecodeString(&slice, &result));
    EXPECT_EQ(test_case, result);
    EXPECT_TRUE(slice.empty());
  }
}

static std::string WrappedEncodeStringWithLength(std::u16string value) {
  std::string buffer;
  EncodeStringWithLength(value, &buffer);
  return buffer;
}

TEST(IndexedDBLevelDBCodingTest, EncodeStringWithLength) {
  const char16_t test_string_a[] = {'f', 'o', 'o', '\0'};
  const char16_t test_string_b[] = {0xdead, 0xbeef, '\0'};

  EXPECT_EQ(1u, WrappedEncodeStringWithLength(u"").size());
  EXPECT_EQ(3u, WrappedEncodeStringWithLength(u"a").size());
  EXPECT_EQ(
      7u, WrappedEncodeStringWithLength(std::u16string(test_string_a)).size());
  EXPECT_EQ(
      5u, WrappedEncodeStringWithLength(std::u16string(test_string_b)).size());
}

TEST(IndexedDBLevelDBCodingTest, DecodeStringWithLength) {
  const char16_t test_string_a[] = {'f', 'o', 'o', '\0'};
  const char16_t test_string_b[] = {0xdead, 0xbeef, '\0'};

  const int kLongStringLen = 1234;
  std::array<char16_t, kLongStringLen + 1> long_string;
  for (int i = 0; i < kLongStringLen; ++i)
    long_string[i] = i;
  long_string[kLongStringLen] = 0;

  std::vector<std::u16string> test_cases = {u"",
                                            u"a",
                                            u"foo",
                                            std::u16string(test_string_a),
                                            std::u16string(test_string_b),
                                            std::u16string(long_string.data())};

  for (size_t i = 0; i < test_cases.size(); ++i) {
    std::u16string s = test_cases[i];
    std::string v = WrappedEncodeStringWithLength(s);
    ASSERT_GT(v.size(), 0u);
    std::string_view slice(v);
    std::u16string res;
    EXPECT_TRUE(DecodeStringWithLength(&slice, &res));
    EXPECT_EQ(s, res);
    EXPECT_TRUE(slice.empty());

    slice = std::string_view(&*v.begin(), v.size() - 1);
    EXPECT_FALSE(DecodeStringWithLength(&slice, &res));

    slice = std::string_view(&*v.begin(), static_cast<size_t>(0));
    EXPECT_FALSE(DecodeStringWithLength(&slice, &res));

    // Verify decoding at an offset, to detect unaligned memory access.
    v.insert(v.begin(), 1u, static_cast<char>(0));
    slice = std::string_view(v).substr(1u);
    EXPECT_TRUE(DecodeStringWithLength(&slice, &res));
    EXPECT_EQ(s, res);
    EXPECT_TRUE(slice.empty());
  }
}

static int CompareStrings(const std::string& p, const std::string& q) {
  bool ok;
  DCHECK(!p.empty());
  DCHECK(!q.empty());
  std::string_view slice_p(p);
  std::string_view slice_q(q);
  int result = CompareEncodedStringsWithLength(&slice_p, &slice_q, &ok);
  EXPECT_TRUE(ok);
  EXPECT_TRUE(slice_p.empty());
  EXPECT_TRUE(slice_q.empty());
  return result;
}

TEST(IndexedDBLevelDBCodingTest, CompareEncodedStringsWithLength) {
  const char16_t test_string_a[] = {0x1000, 0x1000, '\0'};
  const char16_t test_string_b[] = {0x1000, 0x1000, 0x1000, '\0'};
  const char16_t test_string_c[] = {0x1000, 0x1000, 0x1001, '\0'};
  const char16_t test_string_d[] = {0x1001, 0x1000, 0x1000, '\0'};
  const char16_t test_string_e[] = {0xd834, 0xdd1e, '\0'};
  const char16_t test_string_f[] = {0xfffd, '\0'};

  std::vector<std::u16string> test_cases = {
      u"",
      u"a",
      u"b",
      u"baaa",
      u"baab",
      u"c",
      std::u16string(test_string_a),
      std::u16string(test_string_b),
      std::u16string(test_string_c),
      std::u16string(test_string_d),
      std::u16string(test_string_e),
      std::u16string(test_string_f),
  };

  for (size_t i = 0; i < test_cases.size() - 1; ++i) {
    std::u16string a = test_cases[i];
    std::u16string b = test_cases[i + 1];

    EXPECT_LT(a.compare(b), 0);
    EXPECT_GT(b.compare(a), 0);
    EXPECT_EQ(a.compare(a), 0);
    EXPECT_EQ(b.compare(b), 0);

    std::string encoded_a = WrappedEncodeStringWithLength(a);
    EXPECT_TRUE(encoded_a.size());
    std::string encoded_b = WrappedEncodeStringWithLength(b);
    EXPECT_TRUE(encoded_a.size());

    EXPECT_LT(CompareStrings(encoded_a, encoded_b), 0);
    EXPECT_GT(CompareStrings(encoded_b, encoded_a), 0);
    EXPECT_EQ(CompareStrings(encoded_a, encoded_a), 0);
    EXPECT_EQ(CompareStrings(encoded_b, encoded_b), 0);
  }
}

static std::string WrappedEncodeBinary(const std::string& value) {
  std::string buffer;
  EncodeBinary(value, &buffer);
  return buffer;
}

TEST(IndexedDBLevelDBCodingTest, EncodeBinary) {
  const auto binary_data =
      std::to_array<unsigned char>({0x00, 0x01, 0xfe, 0xff});

  EXPECT_EQ(1u, WrappedEncodeBinary(
                    std::string(binary_data.data(),
                                base::span(binary_data).subspan(0u).data()))
                    .size());

  EXPECT_EQ(2u, WrappedEncodeBinary(
                    std::string(binary_data.data(),
                                base::span(binary_data).subspan(1u).data()))
                    .size());

  EXPECT_EQ(5u, WrappedEncodeBinary(
                    std::string(binary_data.data(),
                                base::span(binary_data).subspan(4u).data()))
                    .size());
}

TEST(IndexedDBLevelDBCodingTest, DecodeBinary) {
  const auto binary_data =
      std::to_array<unsigned char>({0x00, 0x01, 0xfe, 0xff});

  std::vector<std::string> test_cases = {
      std::string(
          binary_data.data(),
          base::span<const unsigned char>(binary_data).subspan(0u).data()),
      std::string(
          binary_data.data(),
          base::span<const unsigned char>(binary_data).subspan(1u).data()),
      std::string(
          binary_data.data(),
          base::span<const unsigned char>(binary_data).subspan(4u).data())};

  for (size_t i = 0; i < test_cases.size(); ++i) {
    std::string value = test_cases[i];
    std::string v = WrappedEncodeBinary(value);
    ASSERT_GT(v.size(), 0u);
    std::string_view slice(v);
    std::string result;
    EXPECT_TRUE(DecodeBinary(&slice, &result));
    EXPECT_EQ(value, result);
    EXPECT_TRUE(slice.empty());

    slice = std::string_view(&*v.begin(), v.size() - 1);
    EXPECT_FALSE(DecodeBinary(&slice, &result));

    slice = std::string_view(&*v.begin(), static_cast<size_t>(0));
    EXPECT_FALSE(DecodeBinary(&slice, &result));

    // Verify decoding at an offset, to detect unaligned memory access.
    v.insert(v.begin(), 1u, static_cast<char>(0));
    slice = std::string_view(v).substr(1u);
    EXPECT_TRUE(DecodeBinary(&slice, &result));
    EXPECT_EQ(value, result);
    EXPECT_TRUE(slice.empty());
  }
}

static std::string WrappedEncodeDouble(double value) {
  std::string buffer;
  EncodeDouble(value, &buffer);
  return buffer;
}

TEST(IndexedDBLevelDBCodingTest, EncodeDouble) {
  EXPECT_EQ(8u, WrappedEncodeDouble(0).size());
  EXPECT_EQ(8u, WrappedEncodeDouble(3.14).size());
}

TEST(IndexedDBLevelDBCodingTest, DecodeDouble) {
  std::vector<double> test_cases = {3.14, -3.14};

  for (size_t i = 0; i < test_cases.size(); ++i) {
    double value = test_cases[i];
    std::string v = WrappedEncodeDouble(value);
    ASSERT_GT(v.size(), 0u);
    std::string_view slice(v);
    double result;
    EXPECT_TRUE(DecodeDouble(&slice, &result));
    EXPECT_EQ(value, result);
    EXPECT_TRUE(slice.empty());

    slice = std::string_view(&*v.begin(), v.size() - 1);
    EXPECT_FALSE(DecodeDouble(&slice, &result));

    slice = std::string_view(&*v.begin(), static_cast<size_t>(0));
    EXPECT_FALSE(DecodeDouble(&slice, &result));

    // Verify decoding at an offset, to detect unaligned memory access.
    v.insert(v.begin(), 1u, static_cast<char>(0));
    slice = std::string_view(v).substr(1u);
    EXPECT_TRUE(DecodeDouble(&slice, &result));
    EXPECT_EQ(value, result);
    EXPECT_TRUE(slice.empty());
  }
}

TEST(IndexedDBLevelDBCodingTest, EncodeDecodeIDBKey) {
  IndexedDBKey expected_key;
  std::string v;
  std::string_view slice;

  IndexedDBKey::KeyArray array;
  array.emplace_back(1234, blink::mojom::IDBKeyType::Number);
  array.emplace_back(7890, blink::mojom::IDBKeyType::Date);
  array.emplace_back(u"Hello World!");
  array.emplace_back(std::string("\x01\x02"));
  array.emplace_back(IndexedDBKey::KeyArray());

  auto test_cases = std::to_array(
      {IndexedDBKey(1234, blink::mojom::IDBKeyType::Number),
       IndexedDBKey(7890, blink::mojom::IDBKeyType::Date),
       IndexedDBKey(u"Hello World!"), IndexedDBKey(std::string("\x01\x02")),
       IndexedDBKey(IndexedDBKey::KeyArray()), IndexedDBKey(std::move(array))});

  for (size_t i = 0; i < test_cases.size(); ++i) {
    expected_key = test_cases[i].Clone();
    v.clear();
    EncodeIDBKey(expected_key, &v);
    slice = std::string_view(&*v.begin(), v.size());
    IndexedDBKey decoded_key = DecodeIDBKey(&slice);
    EXPECT_TRUE(decoded_key.IsValid());
    EXPECT_TRUE(decoded_key.Equals(expected_key));
    EXPECT_TRUE(slice.empty());

    slice = std::string_view(&*v.begin(), v.size() - 1);
    EXPECT_FALSE(DecodeIDBKey(&slice).IsValid());

    slice = std::string_view(&*v.begin(), static_cast<size_t>(0));
    EXPECT_FALSE(DecodeIDBKey(&slice).IsValid());
  }
}

static std::string WrappedEncodeIDBKeyPath(const IndexedDBKeyPath& value) {
  std::string buffer;
  EncodeIDBKeyPath(value, &buffer);
  return buffer;
}

TEST(IndexedDBLevelDBCodingTest, EncodeDecodeIDBKeyPath) {
  std::vector<IndexedDBKeyPath> key_paths;
  std::vector<std::string> encoded_paths;

  {
    key_paths.push_back(IndexedDBKeyPath());
    char expected[] = {0, 0,  // Header
                       0      // Type is null
    };
    encoded_paths.push_back(
        std::string(std::begin(expected), std::end(expected)));
  }

  {
    key_paths.push_back(IndexedDBKeyPath(u""));
    char expected[] = {0, 0,  // Header
                       1,     // Type is string
                       0      // Length is 0
    };
    encoded_paths.push_back(
        std::string(std::begin(expected), std::end(expected)));
  }

  {
    key_paths.emplace_back(u"foo");
    char expected[] = {0, 0,                      // Header
                       1,                         // Type is string
                       3, 0, 'f', 0, 'o', 0, 'o'  // String length 3, UTF-16BE
    };
    encoded_paths.push_back(
        std::string(std::begin(expected), std::end(expected)));
  }

  {
    key_paths.emplace_back(u"foo.bar");
    char expected[] = {0, 0,  // Header
                       1,     // Type is string
                       7, 0, 'f', 0, 'o', 0, 'o', 0, '.', 0, 'b', 0, 'a', 0,
                       'r'  // String length 7, UTF-16BE
    };
    encoded_paths.push_back(
        std::string(std::begin(expected), std::end(expected)));
  }

  {
    std::vector<std::u16string> array = {u"", u"foo", u"foo.bar"};

    key_paths.push_back(IndexedDBKeyPath(array));
    char expected[] = {0, 0,                       // Header
                       2, 3,                       // Type is array, length is 3
                       0,                          // Member 1 (String length 0)
                       3, 0, 'f', 0, 'o', 0, 'o',  // Member 2 (String length 3)
                       7, 0, 'f', 0, 'o', 0, 'o', 0, '.', 0, 'b', 0, 'a', 0,
                       'r'  // Member 3 (String length 7)
    };
    encoded_paths.push_back(
        std::string(std::begin(expected), std::end(expected)));
  }

  ASSERT_EQ(key_paths.size(), encoded_paths.size());
  for (size_t i = 0; i < key_paths.size(); ++i) {
    IndexedDBKeyPath key_path = key_paths[i];
    std::string encoded = encoded_paths[i];

    std::string v = WrappedEncodeIDBKeyPath(key_path);
    EXPECT_EQ(encoded, v);

    std::string_view slice(encoded);
    IndexedDBKeyPath decoded;
    EXPECT_TRUE(DecodeIDBKeyPath(&slice, &decoded));
    EXPECT_EQ(key_path, decoded);
    EXPECT_TRUE(slice.empty());
  }
}

TEST(IndexedDBLevelDBCodingTest, EncodeDecodeBlobJournal) {
  std::vector<IndexedDBKeyPath> key_paths;
  std::vector<std::string> encoded_paths;

  std::vector<BlobJournalType> journals;

  {  // Empty journal
    journals.push_back({});
  }

  {  // One item
    journals.push_back({{4, 7}});
  }

  {  // kAllBlobsKey
    journals.push_back({{5, DatabaseMetaDataKey::kAllBlobsNumber}});
  }

  {  // A bunch of items
    journals.push_back(
        {{4, 7}, {5, 6}, {4, 5}, {4, 4}, {1, 12}, {4, 3}, {15, 14}});
  }

  for (const auto& journal_iter : journals) {
    std::string encoding;
    EncodeBlobJournal(journal_iter, &encoding);
    std::string_view slice(encoding);
    BlobJournalType journal_out;
    EXPECT_TRUE(DecodeBlobJournal(&slice, &journal_out));
    EXPECT_EQ(journal_iter, journal_out);
  }

  journals.clear();

  {  // Illegal database id
    journals.push_back({{0, 3}});
  }

  {  // Illegal blob id
    journals.push_back({{4, 0}});
  }

  for (const auto& journal_iter : journals) {
    std::string encoding;
    EncodeBlobJournal(journal_iter, &encoding);
    std::string_view slice(encoding);
    BlobJournalType journal_out;
    EXPECT_FALSE(DecodeBlobJournal(&slice, &journal_out));
  }
}

TEST(IndexedDBLevelDBCodingTest, DecodeLegacyIDBKeyPath) {
  // Legacy encoding of string key paths.
  std::vector<IndexedDBKeyPath> key_paths;
  std::vector<std::string> encoded_paths;

  {
    key_paths.push_back(IndexedDBKeyPath(u""));
    encoded_paths.push_back(std::string());
  }
  {
    key_paths.emplace_back(u"foo");
    char expected[] = {0, 'f', 0, 'o', 0, 'o'};
    encoded_paths.push_back(std::string(expected, std::size(expected)));
  }
  {
    key_paths.emplace_back(u"foo.bar");
    char expected[] = {0, 'f', 0, 'o', 0, 'o', 0, '.', 0, 'b', 0, 'a', 0, 'r'};
    encoded_paths.push_back(std::string(expected, std::size(expected)));
  }

  ASSERT_EQ(key_paths.size(), encoded_paths.size());
  for (size_t i = 0; i < key_paths.size(); ++i) {
    IndexedDBKeyPath key_path = key_paths[i];
    std::string encoded = encoded_paths[i];

    std::string_view slice(encoded);
    IndexedDBKeyPath decoded;
    EXPECT_TRUE(DecodeIDBKeyPath(&slice, &decoded));
    EXPECT_EQ(key_path, decoded);
    EXPECT_TRUE(slice.empty());
  }
}

TEST(IndexedDBLevelDBCodingTest, ExtractAndCompareIDBKeys) {
  auto keys = std::to_array({
      IndexedDBKey(-10, blink::mojom::IDBKeyType::Number),
      IndexedDBKey(0, blink::mojom::IDBKeyType::Number),
      IndexedDBKey(3.14, blink::mojom::IDBKeyType::Number),

      IndexedDBKey(0, blink::mojom::IDBKeyType::Date),
      IndexedDBKey(100, blink::mojom::IDBKeyType::Date),
      IndexedDBKey(100000, blink::mojom::IDBKeyType::Date),

      IndexedDBKey(u""),
      IndexedDBKey(u"a"),
      IndexedDBKey(u"b"),
      IndexedDBKey(u"baaa"),
      IndexedDBKey(u"baab"),
      IndexedDBKey(u"c"),

      IndexedDBKey(std::string()),
      IndexedDBKey(std::string("\x01")),
      IndexedDBKey(std::string("\x01\x01")),
      IndexedDBKey(std::string("\x01\x02")),
      IndexedDBKey(std::string("\x02")),
      IndexedDBKey(std::string("\x02\x01")),
      IndexedDBKey(std::string("\x02\x02")),
      IndexedDBKey(std::string("\xff")),

      CreateArrayIDBKey(),

      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Number)),

      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Number),
                        IndexedDBKey(3.14, blink::mojom::IDBKeyType::Number)),

      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Date)),

      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Date),
                        IndexedDBKey(0, blink::mojom::IDBKeyType::Date)),
      CreateArrayIDBKey(IndexedDBKey(u"")),
      CreateArrayIDBKey(IndexedDBKey(u""), IndexedDBKey(u"a")),
      CreateArrayIDBKey(CreateArrayIDBKey()),
      CreateArrayIDBKey(CreateArrayIDBKey(), CreateArrayIDBKey()),
      CreateArrayIDBKey(CreateArrayIDBKey(CreateArrayIDBKey())),
      CreateArrayIDBKey(
          CreateArrayIDBKey(CreateArrayIDBKey(CreateArrayIDBKey()))),
  });

  for (size_t i = 0; i < keys.size() - 1; ++i) {
    const IndexedDBKey& key_a = keys[i];
    const IndexedDBKey& key_b = keys[i + 1];

    EXPECT_TRUE(key_a.IsLessThan(key_b));

    std::string encoded_a;
    EncodeIDBKey(key_a, &encoded_a);
    EXPECT_TRUE(encoded_a.size());
    std::string encoded_b;
    EncodeIDBKey(key_b, &encoded_b);
    EXPECT_TRUE(encoded_b.size());

    std::string extracted_a;
    std::string extracted_b;
    std::string_view slice;

    slice = std::string_view(encoded_a);
    EXPECT_TRUE(ExtractEncodedIDBKey(&slice, &extracted_a));
    EXPECT_TRUE(slice.empty());
    EXPECT_EQ(encoded_a, extracted_a);

    slice = std::string_view(encoded_b);
    EXPECT_TRUE(ExtractEncodedIDBKey(&slice, &extracted_b));
    EXPECT_TRUE(slice.empty());
    EXPECT_EQ(encoded_b, extracted_b);

    EXPECT_LT(CompareKeys(extracted_a, extracted_b), 0);
    EXPECT_GT(CompareKeys(extracted_b, extracted_a), 0);
    EXPECT_EQ(CompareKeys(extracted_a, extracted_a), 0);
    EXPECT_EQ(CompareKeys(extracted_b, extracted_b), 0);

    slice = std::string_view(&*encoded_a.begin(), encoded_a.size() - 1);
    EXPECT_FALSE(ExtractEncodedIDBKey(&slice, &extracted_a));
  }
}

// Basic verification that the variable length encoding for strings is working
// as expected.
TEST(IndexedDBLevelDBCodingTest, EncodeSortableString) {
  // Two equal length strings that only use characters < 127 have the same
  // length when encoded.
  EXPECT_EQ(EncodeSortableIDBKey(IndexedDBKey(u"Hello world")).size(),
            EncodeSortableIDBKey(IndexedDBKey(u"Hello w0rld")).size());

  // But when one string uses a character >= 127, that takes up another byte.
  EXPECT_EQ(EncodeSortableIDBKey(IndexedDBKey(u"Hello world")).size(),
            EncodeSortableIDBKey(IndexedDBKey(u"H\x82llo world")).size() - 1);

  // A character that doesn't fit in 14 bits uses 3 bytes.
  EXPECT_EQ(EncodeSortableIDBKey(IndexedDBKey(u"Hello world")).size(),
            EncodeSortableIDBKey(IndexedDBKey(u"H\xf082llo world")).size() - 2);
}

TEST(IndexedDBLevelDBCodingTest, EncodeSortableBinary) {
  static constexpr size_t kBinarySize = 17;
  std::vector<uint64_t> binary_input;
  binary_input.reserve(kBinarySize);
  base::test::InsecureRandomGenerator gen;
  gen.ReseedForTesting(0xfedcba9876543210);
  for (size_t i = 0; i < kBinarySize; ++i) {
    binary_input.push_back(gen.RandUint64());
  }

  for (std::string_view sv(reinterpret_cast<const char*>(binary_input.data()),
                           binary_input.size() * sizeof(uint64_t));
       ; sv.remove_prefix(1)) {
    std::string encoded = EncodeSortableIDBKey(IndexedDBKey(std::string(sv)));
    // The binary encoding always takes a multiple of 9 bytes, plus a sentinel
    // byte, plus a type byte.
    EXPECT_EQ(encoded.size() % 9, 2U);
    blink::IndexedDBKey decoded = DecodeSortableIDBKey(encoded);
    EXPECT_TRUE(decoded.IsValid());
    EXPECT_TRUE(decoded.Equals(IndexedDBKey(std::string(sv))));

    if (sv.empty()) {
      break;
    }
  }
}

TEST(IndexedDBLevelDBCodingTest, EncodeAndCompareIDBKeysWithSentinels) {
  const char16_t kJunkString[] = {0xdead, 0xbeef, '\0'};

  auto keys = std::to_array({
      IndexedDBKey(-15, blink::mojom::IDBKeyType::Number),
      IndexedDBKey(-10, blink::mojom::IDBKeyType::Number),
      IndexedDBKey(0, blink::mojom::IDBKeyType::Number),
      IndexedDBKey(3.14, blink::mojom::IDBKeyType::Number),
      IndexedDBKey(42, blink::mojom::IDBKeyType::Number),

      IndexedDBKey(0, blink::mojom::IDBKeyType::Date),
      IndexedDBKey(100, blink::mojom::IDBKeyType::Date),
      IndexedDBKey(100000, blink::mojom::IDBKeyType::Date),

      IndexedDBKey(u""),
      IndexedDBKey(u"a"),
      IndexedDBKey(u"b"),
      IndexedDBKey(u"baaa"),
      IndexedDBKey(u"baab"),
      IndexedDBKey(u"c"),

      // Some more adventurous strings.
      IndexedDBKey(u"\xA2"),
      // Valid UTF16.
      IndexedDBKey(u"\x4f60\x597d "),
      // Invalid UTF16. The first character is a truncated UTF-16 character.
      IndexedDBKey(u"\xd800\x597d"),
      IndexedDBKey(std::u16string(kJunkString)),

      IndexedDBKey(std::string()),
      IndexedDBKey(std::string("\x01")),
      IndexedDBKey(std::string("\x01\x01")),
      IndexedDBKey(std::string("\x01\x02")),
      IndexedDBKey(std::string("\x02")),
      IndexedDBKey(std::string("\x02\x01")),
      IndexedDBKey(std::string("\x02\x02")),
      // Same as previous binary, but with added null byte at end.
      IndexedDBKey(std::string("\x02\x02\x00", 3)),
      IndexedDBKey(std::string("Lorem ipsum and some bits"
                               "\x01\x02\x03\x04\x05\x06\x07")),
      IndexedDBKey(std::string("\xff")),

      CreateArrayIDBKey(),

      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Number)),

      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Number),
                        IndexedDBKey(3.14, blink::mojom::IDBKeyType::Number)),

      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Date)),

      CreateArrayIDBKey(IndexedDBKey(0, blink::mojom::IDBKeyType::Date),
                        IndexedDBKey(0, blink::mojom::IDBKeyType::Date)),
      CreateArrayIDBKey(IndexedDBKey(u"")),
      CreateArrayIDBKey(IndexedDBKey(u""), IndexedDBKey(u"a")),
      CreateArrayIDBKey(CreateArrayIDBKey()),
      CreateArrayIDBKey(CreateArrayIDBKey(), CreateArrayIDBKey()),
      CreateArrayIDBKey(CreateArrayIDBKey(CreateArrayIDBKey())),
      CreateArrayIDBKey(
          CreateArrayIDBKey(CreateArrayIDBKey(CreateArrayIDBKey()))),
  });

  for (size_t i = 0; i < keys.size(); ++i) {
    const IndexedDBKey& key_a = keys[i];
    std::string encoded_a = EncodeSortableIDBKey(key_a);
    EXPECT_TRUE(encoded_a.size());

    ASSERT_TRUE(DecodeSortableIDBKey(encoded_a).IsValid());
    EXPECT_TRUE(DecodeSortableIDBKey(encoded_a).Equals(key_a));

    if (i == keys.size() - 1) {
      break;
    }

    const IndexedDBKey& key_b = keys[i + 1];
    SCOPED_TRACE(testing::Message() << "Comparing keys " << key_a.DebugString()
                                    << " and " << key_b.DebugString());

    EXPECT_TRUE(key_a.IsLessThan(key_b));
    std::string encoded_b = EncodeSortableIDBKey(key_b);
    EXPECT_TRUE(encoded_b.size());

    auto sqlite_compare = [](const std::string& a, const std::string& b) {
      return UNSAFE_TODO(
          std::memcmp(a.c_str(), b.c_str(), std::min(a.length(), b.length())));
    };

    EXPECT_LT(sqlite_compare(encoded_a, encoded_b), 0);
    EXPECT_GT(sqlite_compare(encoded_b, encoded_a), 0);
    EXPECT_EQ(sqlite_compare(encoded_a, encoded_a), 0);
    EXPECT_EQ(sqlite_compare(encoded_b, encoded_b), 0);
  }

  std::vector<IndexedDBKey> keys_vec;
  for (const auto& key : keys) {
    keys_vec.emplace_back(key.Clone());
  }
  // Also test decoding by treating all test cases as one massive array key.
  const IndexedDBKey all_keys_key(std::move(keys_vec));
  std::string encoded = EncodeSortableIDBKey(all_keys_key);
  IndexedDBKey decoded_value = DecodeSortableIDBKey(encoded);
  ASSERT_TRUE(decoded_value.IsValid());
  EXPECT_TRUE(all_keys_key.Equals(decoded_value))
      << "Original is\n"
      << all_keys_key.DebugString() << "\nwhereas depickled version is\n"
      << decoded_value.DebugString();
}

TEST(IndexedDBLevelDBCodingTest, DecodeSortableWithCorruption) {
  std::vector<std::string> cases = {
      // Empty string.
      {},
      // Binary with bad meta-mark.
      {"\x40\x02\xff\x00", 4},
      // String with bad meta-mark.
      {"\x30\x00\x02\xff\xff\x00\x00", 7},
      // Array without terminating sentinel.
      {"\x50\x20\xff\xff\xff\xff", 6},
      // String with no terminating sentinel.
      {"\x30\x00\x01\xff\xff", 5},
      // Double with insufficient bytes.
      {"\x10\x00\x01\xff", 4},
  };

  for (const auto& test_case : cases) {
    EXPECT_FALSE(DecodeSortableIDBKey(test_case).IsValid());
  }
}

// Verify that encoded doubles compare in the same order as C++ double
// arithmetic.
TEST(IndexedDBLevelDBCodingTest, EncodeSortableDoubles) {
  std::vector<double> values = {
      0.0,
      -0.0,
      1.0,
      -1.0,

      std::numeric_limits<double>::infinity(),
      -std::numeric_limits<double>::infinity(),
      std::numeric_limits<double>::lowest(),
      std::numeric_limits<double>::max(),

      std::numeric_limits<double>::min(),
      -std::numeric_limits<double>::min(),
      std::numeric_limits<double>::min() * 10,
      -std::numeric_limits<double>::min() * 10,

      std::numeric_limits<double>::denorm_min(),
      -std::numeric_limits<double>::denorm_min(),
      std::numeric_limits<double>::denorm_min() * 10,
      -std::numeric_limits<double>::denorm_min() * 10,
  };

  for (double value_a : values) {
    for (double value_b : values) {
      SCOPED_TRACE(testing::Message()
                   << "Comparing " << value_a << " and " << value_b);

      std::string encoded_a = EncodeSortableIDBKey(
          IndexedDBKey(value_a, blink::mojom::IDBKeyType::Number));
      EXPECT_TRUE(encoded_a.size());
      std::string encoded_b = EncodeSortableIDBKey(
          IndexedDBKey(value_b, blink::mojom::IDBKeyType::Number));
      EXPECT_TRUE(encoded_b.size());
      EXPECT_EQ(encoded_a.size(), encoded_b.size());

      auto sqlite_compare = [](const std::string& a, const std::string& b) {
        return UNSAFE_TODO(std::memcmp(a.c_str(), b.c_str(),
                                       std::min(a.length(), b.length())));
      };

      if (value_a < value_b) {
        EXPECT_LT(sqlite_compare(encoded_a, encoded_b), 0);
      } else if (value_a == value_b) {
        EXPECT_EQ(sqlite_compare(encoded_a, encoded_b), 0);
      } else {
        EXPECT_GT(sqlite_compare(encoded_a, encoded_b), 0);
      }
    }
  }

  for (double value : values) {
    const IndexedDBKey key(value, blink::mojom::IDBKeyType::Number);
    std::string encoded = EncodeSortableIDBKey(key);
    IndexedDBKey decoded_value = DecodeSortableIDBKey(encoded);
    ASSERT_TRUE(decoded_value.IsValid());
    EXPECT_TRUE(key.Equals(decoded_value))
        << "Original is\n"
        << key.DebugString() << "\nwhereas depickled version is\n"
        << decoded_value.DebugString();
  }
}

TEST(IndexedDBLevelDBCodingTest, ComparisonTest) {
  std::vector<std::string> keys = {
      SchemaVersionKey::Encode(),
      MaxDatabaseIdKey::Encode(),
      DatabaseFreeListKey::Encode(0),
      DatabaseFreeListKey::EncodeMaxKey(),
      DatabaseNameKey::Encode("", u""),
      DatabaseNameKey::Encode("", u"a"),
      DatabaseNameKey::Encode("a", u"a"),

      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::ORIGIN_NAME),

      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::DATABASE_NAME),

      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::USER_STRING_VERSION),

      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::MAX_OBJECT_STORE_ID),

      DatabaseMetaDataKey::Encode(1, DatabaseMetaDataKey::USER_VERSION),

      ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::NAME),

      ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::KEY_PATH),
      ObjectStoreMetaDataKey::Encode(1, 1,
                                     ObjectStoreMetaDataKey::AUTO_INCREMENT),

      ObjectStoreMetaDataKey::Encode(1, 1, ObjectStoreMetaDataKey::EVICTABLE),
      ObjectStoreMetaDataKey::Encode(1, 1,
                                     ObjectStoreMetaDataKey::LAST_VERSION),
      ObjectStoreMetaDataKey::Encode(1, 1,
                                     ObjectStoreMetaDataKey::MAX_INDEX_ID),
      ObjectStoreMetaDataKey::Encode(1, 1,
                                     ObjectStoreMetaDataKey::HAS_KEY_PATH),
      ObjectStoreMetaDataKey::Encode(
          1, 1, ObjectStoreMetaDataKey::KEY_GENERATOR_CURRENT_NUMBER),
      ObjectStoreMetaDataKey::EncodeMaxKey(1, 1),
      ObjectStoreMetaDataKey::EncodeMaxKey(1, 2),
      ObjectStoreMetaDataKey::EncodeMaxKey(1),
      IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::NAME),
      IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::UNIQUE),

      IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::KEY_PATH),

      IndexMetaDataKey::Encode(1, 1, 30, IndexMetaDataKey::MULTI_ENTRY),
      IndexMetaDataKey::Encode(1, 1, 31, 0),
      IndexMetaDataKey::Encode(1, 1, 31, 1),
      IndexMetaDataKey::EncodeMaxKey(1, 1, 31),
      IndexMetaDataKey::EncodeMaxKey(1, 1, 32),
      IndexMetaDataKey::EncodeMaxKey(1, 1),
      IndexMetaDataKey::EncodeMaxKey(1, 2),
      ObjectStoreFreeListKey::Encode(1, 1),
      ObjectStoreFreeListKey::EncodeMaxKey(1),
      IndexFreeListKey::Encode(1, 1, kMinimumIndexId),
      IndexFreeListKey::EncodeMaxKey(1, 1),
      IndexFreeListKey::Encode(1, 2, kMinimumIndexId),
      IndexFreeListKey::EncodeMaxKey(1, 2),
      ObjectStoreNamesKey::Encode(1, u""),
      ObjectStoreNamesKey::Encode(1, u"a"),
      IndexNamesKey::Encode(1, 1, u""),
      IndexNamesKey::Encode(1, 1, u"a"),
      IndexNamesKey::Encode(1, 2, u"a"),
      ObjectStoreDataKey::Encode(1, 1, MinIDBKey()),
      ObjectStoreDataKey::Encode(1, 1, MaxIDBKey()),
      ExistsEntryKey::Encode(1, 1, MinIDBKey()),
      ExistsEntryKey::Encode(1, 1, MaxIDBKey()),
      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), std::string(), 0),
      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 0),
      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 1),
      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 0),
      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 1),
      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 0),
      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 1),
      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 0),
      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 1),
      IndexDataKey::Encode(1, 1, 31, MinIDBKey(), MinIDBKey(), 0),
      IndexDataKey::Encode(1, 2, 30, MinIDBKey(), MinIDBKey(), 0),
      IndexDataKey::EncodeMaxKey(1, 2, std::numeric_limits<int32_t>::max() - 1),
  };

  for (size_t i = 0; i < keys.size(); ++i) {
    EXPECT_EQ(Compare(keys[i], keys[i], false), 0);

    for (size_t j = i + 1; j < keys.size(); ++j) {
      EXPECT_LT(Compare(keys[i], keys[j], false), 0);
      EXPECT_GT(Compare(keys[j], keys[i], false), 0);
    }
  }
}

TEST(IndexedDBLevelDBCodingTest, CompareWithCorruptIndexDataKey) {
  std::string index_data_key_with_positive_sequence_number =
      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(),
                           std::numeric_limits<int64_t>::max());

  // `IndexDataKey` must use a positive sequence number.  Simulate disk
  // corruption with an encoded negative sequence number.
  std::string index_data_key_with_negative_sequence_number;
  ASSERT_TRUE(base::Base64Decode(
      "AwMD/////wD+ioqKioqKirUBAAAAAAAAioqKioqKioqKioqKioqKioqKioqKioqKioo=",
      &index_data_key_with_negative_sequence_number));

  EXPECT_EQ(Compare(index_data_key_with_positive_sequence_number,
                    index_data_key_with_negative_sequence_number,
                    /*only_compare_index_keys=*/false),
            -1);
}

TEST(IndexedDBLevelDBCodingTest, IndexDataKeyEncodeDecode) {
  std::vector<std::string> keys = {
      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 0),
      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MinIDBKey(), 1),

      IndexDataKey::Encode(1, 1, 30, IndexedDBKey(u"user key"),
                           IndexedDBKey(u"primary key")),
      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 0),
      IndexDataKey::Encode(1, 1, 30, MinIDBKey(), MaxIDBKey(), 1),
      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 0),
      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MinIDBKey(), 1),
      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 0),
      IndexDataKey::Encode(1, 1, 30, MaxIDBKey(), MaxIDBKey(), 1),
      IndexDataKey::Encode(1, 1, 31, MinIDBKey(), MinIDBKey(), 0),
      IndexDataKey::Encode(1, 2, 30, MinIDBKey(), MinIDBKey(), 0),
      IndexDataKey::EncodeMaxKey(1, 2, std::numeric_limits<int32_t>::max() - 1),
  };

  std::vector<IndexDataKey> obj_keys;
  for (const std::string& key : keys) {
    std::string_view piece(key);
    IndexDataKey obj_key;
    EXPECT_TRUE(IndexDataKey::Decode(&piece, &obj_key));
    obj_keys.push_back(std::move(obj_key));
  }

  for (size_t i = 0; i < keys.size(); ++i) {
    EXPECT_EQ(keys[i], obj_keys[i].Encode()) << "key at " << i;
  }
}

TEST(IndexedDBLevelDBCodingTest, EncodeVarIntVSEncodeByteTest) {
  std::vector<unsigned char> test_cases = {0, 1, 127};

  for (size_t i = 0; i < test_cases.size(); ++i) {
    unsigned char n = test_cases[i];

    std::string a = WrappedEncodeByte(n);
    std::string b;
    EncodeVarInt(static_cast<int64_t>(n), &b);

    EXPECT_EQ(a.size(), b.size());
    EXPECT_EQ(*a.begin(), *b.begin());
  }
}

TEST(IndexedDBLevelDBCodingTest, Empty) {
  EXPECT_EQ(KeyPrefix::EncodeInternal(0, 0, 0), KeyPrefix::EncodeEmpty());
}

TEST(IndexedDBLevelDBCodingTest, CompareEncodedIDBKeysInvalidTypeByte) {
  // Valid type bytes are 0-6. Byte value 7 and above are invalid.
  constexpr unsigned char kInvalidTypeByte = 7;

  std::string valid_key;
  EncodeIDBKey(IndexedDBKey(u"valid key"), &valid_key);

  std::string invalid_key;
  EncodeByte(kInvalidTypeByte, &invalid_key);

  // Comparing with invalid key in first position should fail.
  {
    std::string_view slice_a(invalid_key);
    std::string_view slice_b(valid_key);
    bool ok = true;
    CompareEncodedIDBKeys(&slice_a, &slice_b, &ok);
    EXPECT_FALSE(ok);
  }

  // Comparing with invalid key in second position should fail.
  {
    std::string_view slice_a(valid_key);
    std::string_view slice_b(invalid_key);
    bool ok = true;
    CompareEncodedIDBKeys(&slice_a, &slice_b, &ok);
    EXPECT_FALSE(ok);
  }

  // Comparing two invalid keys should also fail.
  {
    std::string_view slice_a(invalid_key);
    std::string_view slice_b(invalid_key);
    bool ok = true;
    CompareEncodedIDBKeys(&slice_a, &slice_b, &ok);
    EXPECT_FALSE(ok);
  }
}

namespace {

// The Compare function should handle any input without crashing since bytes
// could have been corrupted on disk.
void CompareHandlesAnyInput(std::string a, std::string b, bool index_keys) {
  Compare(a, b, index_keys);
}
}  // namespace

FUZZ_TEST(IndexedDbLevelDbCodingFuzzTest, CompareHandlesAnyInput);

}  // namespace content::indexed_db
