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

#include <string.h>

#include <map>
#include <vector>

#include <gtest/gtest.h>

#include <libavb_ab/libavb_ab.h>

#include "avb_unittest_util.h"
#include "fake_avb_ops.h"

namespace avb {

static_assert(sizeof(AvbABSlotData) == 4, "AvbABSlotData has wrong size");
static_assert(sizeof(AvbABData) == AVB_AB_DATA_SIZE,
              "AvbABData has wrong size");
static_assert(offsetof(AvbABData, slots) % 8 == 0,
              "AvbABData slots member has wrong offset");

// Subclass BaseAvbToolTest to check for memory leaks.
class ABTest : public BaseAvbToolTest {
 public:
  ABTest() {}
};

TEST_F(ABTest, InitData) {
  AvbABData data;
  avb_ab_data_init(&data);
  EXPECT_EQ(0,
            strncmp(reinterpret_cast<const char*>(data.magic),
                    AVB_AB_MAGIC,
                    AVB_AB_MAGIC_LEN));
  EXPECT_EQ(AVB_AB_MAX_PRIORITY, data.slots[0].priority);
  EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, data.slots[0].tries_remaining);
  EXPECT_EQ(0, data.slots[0].successful_boot);
  EXPECT_EQ(AVB_AB_MAX_PRIORITY - 1, data.slots[1].priority);
  EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, data.slots[1].tries_remaining);
  EXPECT_EQ(0, data.slots[1].successful_boot);
  EXPECT_EQ(uint32_t(0), data.crc32);
}

TEST_F(ABTest, DataSerialization) {
  AvbABData data;
  AvbABData serialized;
  AvbABData restored;

  avb_ab_data_init(&data);
  EXPECT_EQ(uint32_t(0), data.crc32);
  avb_ab_data_update_crc_and_byteswap(&data, &serialized);
  EXPECT_NE(uint32_t(0), serialized.crc32);
  EXPECT_TRUE(avb_ab_data_verify_and_byteswap(&serialized, &restored));
  EXPECT_EQ(std::string(reinterpret_cast<const char*>(data.magic), 4),
            std::string(reinterpret_cast<const char*>(restored.magic), 4));
  EXPECT_EQ(data.version_major, restored.version_major);
  EXPECT_EQ(data.version_minor, restored.version_minor);
  EXPECT_EQ(0,
            memcmp(reinterpret_cast<void*>(data.slots),
                   reinterpret_cast<void*>(restored.slots),
                   sizeof(AvbABSlotData) * 2));
}

TEST_F(ABTest, CatchBadCRC) {
  AvbABData data;
  AvbABData serialized;
  AvbABData restored;

  avb_ab_data_init(&data);
  avb_ab_data_update_crc_and_byteswap(&data, &serialized);
  serialized.crc32 += 1;
  EXPECT_FALSE(avb_ab_data_verify_and_byteswap(&serialized, &restored));
}

TEST_F(ABTest, CatchUnsupportedMajorVersion) {
  AvbABData data;
  AvbABData serialized;
  AvbABData restored;

  avb_ab_data_init(&data);
  data.version_major += 1;
  avb_ab_data_update_crc_and_byteswap(&data, &serialized);
  EXPECT_FALSE(avb_ab_data_verify_and_byteswap(&serialized, &restored));
}

TEST_F(ABTest, SupportSameMajorFutureMinorVersion) {
  AvbABData data;
  AvbABData serialized;
  AvbABData restored;

  avb_ab_data_init(&data);
  data.version_minor += 1;
  avb_ab_data_update_crc_and_byteswap(&data, &serialized);
  EXPECT_TRUE(avb_ab_data_verify_and_byteswap(&serialized, &restored));
}

#define MISC_PART_SIZE 8 * 1024

// These values are kept short since they are used in SetMD() and it's
// helpful if the information for a slot fits in one 80-character
// line.
enum SlotValidity {
  SV_OK,   // Slot is valid and verified.
  SV_INV,  // Slot is invalid.
  SV_UNV,  // Slot is valid but unverified.
};

class AvbABFlowTest : public BaseAvbToolTest {
 public:
  AvbABFlowTest() {}

  virtual void SetUp() override {
    BaseAvbToolTest::SetUp();
    ops_.set_partition_dir(testdir_);
    ops_.set_stored_rollback_indexes({{0, 0}, {1, 0}, {2, 0}, {3, 0}});
    ops_.set_stored_is_device_unlocked(false);

    // Create large enough 'misc' partition and initialize it with
    // zeroes.
    std::vector<uint8_t> misc;
    misc.resize(MISC_PART_SIZE);
    base::FilePath misc_path = testdir_.Append("misc.img");
    EXPECT_EQ(misc.size(),
              static_cast<const size_t>(
                  base::WriteFile(misc_path,
                                  reinterpret_cast<const char*>(misc.data()),
                                  misc.size())));

    // We're going to use this key for all images.
    ops_.set_expected_public_key(
        PublicKeyAVB(base::FilePath("test/data/testkey_rsa2048.pem")));
  }

  void GenerateSlot(unsigned int slot_number,
                    SlotValidity slot_validity,
                    uint64_t rollback_boot,
                    uint64_t rollback_odm) {
    std::string boot_name = "boot_a.img";
    std::string vbmeta_name = "vbmeta_a.img";
    std::string odm_name = "odm_a.img";
    if (slot_number > 0) {
      boot_name = "boot_b.img";
      vbmeta_name = "vbmeta_b.img";
      odm_name = "odm_b.img";
    }

    // If asked to make an invalid slot, just generate 1MiB garbage
    // for each the three images in the slot.
    if (slot_validity == SV_INV) {
      GenerateImage(boot_name, 1024 * 1024);
      GenerateImage(vbmeta_name, 1024 * 1024);
      GenerateImage(odm_name, 1024 * 1024);
      return;
    }

    const size_t boot_partition_size = 16 * 1024 * 1024;
    const size_t boot_image_size = 5 * 1024 * 1024;
    base::FilePath boot_path = GenerateImage(boot_name, boot_image_size);
    EXPECT_COMMAND(0,
                   "./avbtool add_hash_footer"
                   " --image %s"
                   " --rollback_index %" PRIu64
                   " --partition_name boot"
                   " --partition_size %zd"
                   " --salt deadbeef",
                   boot_path.value().c_str(),
                   rollback_boot,
                   boot_partition_size);

    const size_t odm_partition_size = 512 * 1024;
    const size_t odm_image_size = 80 * 1024;
    base::FilePath odm_path = GenerateImage(odm_name, odm_image_size);
    EXPECT_COMMAND(0,
                   "./avbtool add_hashtree_footer"
                   " --image %s"
                   " --rollback_index %" PRIu64
                   " --partition_name odm"
                   " --partition_size %zd"
                   " --salt deadbeef"
                   " --algorithm SHA512_RSA4096 "
                   " --key test/data/testkey_rsa4096.pem"
                   " --do_not_generate_fec",
                   odm_path.value().c_str(),
                   rollback_odm,
                   odm_partition_size);

    base::FilePath pk_path = testdir_.Append("testkey_rsa4096.avbpubkey");
    EXPECT_COMMAND(
        0,
        "./avbtool extract_public_key --key test/data/testkey_rsa4096.pem"
        " --output %s",
        pk_path.value().c_str());

    // If requested to make the image unverified, just use another key
    // in the chain_partition descriptor since this will cause
    // avb_slot_verify() to return ERROR_PUBLIC_KEY_REJECTED.
    if (slot_validity == SV_UNV) {
      pk_path = GenerateImage("dummy.avbpubkey", 32);
    }

    GenerateVBMetaImage(vbmeta_name,
                        "SHA256_RSA2048",
                        rollback_boot,
                        base::FilePath("test/data/testkey_rsa2048.pem"),
                        base::StringPrintf("--include_descriptors_from_image %s"
                                           " --chain_partition odm:1:%s",
                                           boot_path.value().c_str(),
                                           pk_path.value().c_str()));
  }

  void SetMD(int a_pri,
             int a_tries,
             bool a_success,
             SlotValidity a_slot_validity,
             uint64_t a_rollback_boot,
             uint64_t a_rollback_odm,
             int b_pri,
             int b_tries,
             bool b_success,
             SlotValidity b_slot_validity,
             uint64_t b_rollback_boot,
             uint64_t b_rollback_odm,
             const std::map<size_t, uint64_t>& stored_rollback_indexes) {
    AvbABData data;
    avb_ab_data_init(&data);
    data.slots[0].priority = a_pri;
    data.slots[0].tries_remaining = a_tries;
    data.slots[0].successful_boot = (a_success ? 1 : 0);
    data.slots[1].priority = b_pri;
    data.slots[1].tries_remaining = b_tries;
    data.slots[1].successful_boot = (b_success ? 1 : 0);
    EXPECT_EQ(AVB_IO_RESULT_OK,
              ops_.avb_ab_ops()->write_ab_metadata(ops_.avb_ab_ops(), &data));
    GenerateSlot(0, a_slot_validity, a_rollback_boot, a_rollback_odm);
    GenerateSlot(1, b_slot_validity, b_rollback_boot, b_rollback_odm);
    ops_.set_stored_rollback_indexes(stored_rollback_indexes);
  }

  std::map<size_t, uint64_t> MakeRollbackIndexes(uint64_t slot_0_value,
                                                 uint64_t slot_1_value) {
    return std::map<size_t, uint64_t>{{0, slot_0_value}, {1, slot_1_value}};
  }

  FakeAvbOps ops_;
};

#define ExpMD(a_pri,                                                          \
              a_tries,                                                        \
              a_success,                                                      \
              b_pri,                                                          \
              b_tries,                                                        \
              b_success,                                                      \
              stored_rollback_indexes)                                        \
  do {                                                                        \
    AvbABData data;                                                           \
    EXPECT_EQ(AVB_IO_RESULT_OK,                                               \
              ops_.avb_ab_ops()->read_ab_metadata(ops_.avb_ab_ops(), &data)); \
    EXPECT_EQ(a_pri, data.slots[0].priority);                                 \
    EXPECT_EQ(a_tries, data.slots[0].tries_remaining);                        \
    EXPECT_EQ(a_success ? 1 : 0, data.slots[0].successful_boot);              \
    EXPECT_EQ(b_pri, data.slots[1].priority);                                 \
    EXPECT_EQ(b_tries, data.slots[1].tries_remaining);                        \
    EXPECT_EQ(b_success ? 1 : 0, data.slots[1].successful_boot);              \
    EXPECT_EQ(stored_rollback_indexes, ops_.get_stored_rollback_indexes());   \
  } while (0);

TEST_F(AvbABFlowTest, MetadataReadAndWrite) {
  AvbABData data;
  AvbABData loaded;

  // First load from an uninitialized 'misc' partition. This should
  // not fail and just returned initialized data.
  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ab_ops(), &loaded));
  EXPECT_EQ(AVB_AB_MAX_PRIORITY, loaded.slots[0].priority);
  EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, loaded.slots[0].tries_remaining);
  EXPECT_EQ(0, loaded.slots[0].successful_boot);
  EXPECT_EQ(AVB_AB_MAX_PRIORITY - 1, loaded.slots[1].priority);
  EXPECT_EQ(AVB_AB_MAX_TRIES_REMAINING, loaded.slots[1].tries_remaining);
  EXPECT_EQ(0, loaded.slots[1].successful_boot);

  // Then initialize and save well-known A/B metadata and check we
  // read back the same thing.
  avb_ab_data_init(&data);
  data.slots[0].priority = 2;
  data.slots[0].tries_remaining = 3;
  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_write(ops_.avb_ab_ops(), &data));
  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_data_read(ops_.avb_ab_ops(), &loaded));
  EXPECT_EQ(2, loaded.slots[0].priority);
  EXPECT_EQ(3, loaded.slots[0].tries_remaining);
}

TEST_F(AvbABFlowTest, EverythingIsValid) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  SetMD(14,
        0,
        1,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        15,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(14,
        0,
        1,  // A: pri, tries, successful
        15,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_b", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Also check the other slot.
  SetMD(15,
        0,
        1,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        0,
        1,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);
}

TEST_F(AvbABFlowTest, NoBootableSlots) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  SetMD(0,
        0,
        0,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        0,
        0,
        0,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        0,
        0,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_EQ(nullptr, data);
}

TEST_F(AvbABFlowTest, TriesRemainingDecreasing) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  SetMD(15,
        3,
        0,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        0,
        0,
        0,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes

  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        2,
        0,  // A: pri, tries, successful
        0,
        0,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Keep counting down...
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        1,
        0,  // A: pri, tries, successful
        0,
        0,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Last try...
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        0,
        0,  // A: pri, tries, successful
        0,
        0,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // And we're out of tries. At this point, (15, 0, 0) is normalized
  // to (0, 0, 0) so expect that.
  EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        0,
        0,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_EQ(nullptr, data);
}

TEST_F(AvbABFlowTest, TryingThenFallback) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  SetMD(15,
        2,
        0,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        1,
        0,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Last try...
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        0,
        0,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // And we're out of tries. Check we fall back to slot B.
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_b", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);
}

TEST_F(AvbABFlowTest, TriesRemainingNotDecreasingIfNotPriority) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  SetMD(15,
        0,
        1,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        7,
        0,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        0,
        1,  // A: pri, tries, successful
        14,
        7,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);
}

TEST_F(AvbABFlowTest, InvalidSlotIsMarkedAsSuch) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  // Slot A is invalid.
  SetMD(15,
        0,
        1,
        SV_INV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_b", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Slot B is invalid.
  SetMD(15,
        0,
        1,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_INV,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        0,
        1,  // A: pri, tries, successful
        0,
        0,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Both slots are invalid.
  SetMD(15,
        0,
        1,
        SV_INV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_INV,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        0,
        0,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_EQ(nullptr, data);
}

TEST_F(AvbABFlowTest, UnverifiedSlotIsMarkedAsSuch) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  // Slot A fails verification.
  SetMD(15,
        0,
        1,
        SV_UNV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_b", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Slot B fails verification.
  SetMD(15,
        0,
        1,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_UNV,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        0,
        1,  // A: pri, tries, successful
        0,
        0,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Both slots fail verification.
  SetMD(15,
        0,
        1,
        SV_UNV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_UNV,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_ERROR_NO_BOOTABLE_SLOTS,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        0,
        0,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_EQ(nullptr, data);
}

TEST_F(AvbABFlowTest, RollbackIndexFailures) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  // Slot A rollback index failure for 'boot'.
  SetMD(15,
        0,
        1,
        SV_OK,
        0,
        2,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        2,
        2,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(2, 2));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(2, 2));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_b", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Slot A rollback index failure for 'odm'.
  SetMD(15,
        0,
        1,
        SV_OK,
        2,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        2,
        2,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(2, 2));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(2, 2));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_b", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);
}

TEST_F(AvbABFlowTest, StoredRollbackIndexBumped) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  SetMD(15,
        0,
        1,
        SV_OK,
        3,
        3,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        3,
        3,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(2, 2));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        0,
        1,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(3, 3));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // The case where different partitions have different rollback
  // index values.
  SetMD(15,
        0,
        1,
        SV_OK,
        4,
        9,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        5,
        7,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        0,
        1,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(4, 7));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // If the slot with the low RI fails verification (or is invalid),
  // check that these low Rollback Indexs are not taken into account
  // after marking it as unbootable.
  SetMD(15,
        0,
        1,
        SV_INV,
        4,
        9,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        5,
        7,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(5, 7));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_b", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);
}

TEST_F(AvbABFlowTest, MarkSlotActive) {
  SetMD(15,
        0,
        1,
        SV_INV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        11,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_active(ops_.avb_ab_ops(), 0));
  ExpMD(15,
        7,
        0,  // A: pri, tries, successful
        11,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes

  // Note how priority of slot A is altered to make room for newly
  // activated slot.
  SetMD(15,
        0,
        1,
        SV_INV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_IO_RESULT_OK, avb_ab_mark_slot_active(ops_.avb_ab_ops(), 1));
  ExpMD(14,
        0,
        1,  // A: pri, tries, successful
        15,
        7,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
}

TEST_F(AvbABFlowTest, MarkSlotUnbootable) {
  SetMD(15,
        0,
        1,
        SV_INV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        11,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_IO_RESULT_OK,
            avb_ab_mark_slot_unbootable(ops_.avb_ab_ops(), 0));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        11,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes

  SetMD(15,
        0,
        1,
        SV_INV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_IO_RESULT_OK,
            avb_ab_mark_slot_unbootable(ops_.avb_ab_ops(), 1));
  ExpMD(15,
        0,
        1,  // A: pri, tries, successful
        0,
        0,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
}

TEST_F(AvbABFlowTest, MarkSlotSuccessful) {
  SetMD(15,
        5,
        0,
        SV_INV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        11,
        3,
        0,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_IO_RESULT_OK,
            avb_ab_mark_slot_successful(ops_.avb_ab_ops(), 0));
  ExpMD(15,
        0,
        1,  // A: pri, tries, successful
        11,
        3,
        0,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes

  SetMD(15,
        5,
        0,
        SV_INV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_IO_RESULT_OK,
            avb_ab_mark_slot_successful(ops_.avb_ab_ops(), 1));
  ExpMD(15,
        5,
        0,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes

  // Marking an unbootable slot (A) as successful won't work (it's a
  // programmer error to do so)... notice however that the unbootable
  // slot is normalized in the process.
  SetMD(0,
        3,
        2,
        SV_INV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_IO_RESULT_OK,
            avb_ab_mark_slot_successful(ops_.avb_ab_ops(), 0));
  ExpMD(0,
        0,
        0,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
}

static AvbABData my_serialized_data;

static AvbIOResult my_write_ab_metadata(AvbABOps* ops,
                                        const struct AvbABData* data) {
  avb_ab_data_update_crc_and_byteswap(data, &my_serialized_data);
  return AVB_IO_RESULT_OK;
}

static AvbIOResult my_read_ab_metadata(AvbABOps* ops, struct AvbABData* data) {
  if (!avb_ab_data_verify_and_byteswap(&my_serialized_data, data)) {
    avb_error(
        "Error validating A/B metadata from persistent storage. "
        "Resetting and writing new A/B metadata to persistent storage.\n");
    avb_ab_data_init(data);
    return my_write_ab_metadata(ops, data);
  }
  return AVB_IO_RESULT_OK;
}

TEST_F(AvbABFlowTest, OtherMetadataStorage) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  // Use our own A/B storage routines (see above).
  ops_.avb_ab_ops()->read_ab_metadata = my_read_ab_metadata;
  ops_.avb_ab_ops()->write_ab_metadata = my_write_ab_metadata;

  SetMD(14,
        0,
        1,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        15,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(14,
        0,
        1,  // A: pri, tries, successful
        15,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_b", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Also check the other slot.
  SetMD(15,
        0,
        1,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_NONE,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        0,
        1,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Check that 'misc' hasn't been written to at all.
  std::string misc_data;
  base::FilePath misc_path = testdir_.Append("misc.img");
  ASSERT_TRUE(base::ReadFileToString(misc_path, &misc_data));
  EXPECT_EQ(size_t(MISC_PART_SIZE), misc_data.size());
  for (size_t n = 0; n < misc_data.size(); n++) {
    ASSERT_EQ(uint8_t(misc_data[n]), 0);
  }
}

TEST_F(AvbABFlowTest, UnlockedUnverifiedSlot) {
  AvbSlotVerifyData* data;
  const char* requested_partitions[] = {"boot", NULL};

  SetMD(14,
        0,
        1,
        SV_OK,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        15,
        0,
        1,
        SV_UNV,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(14,
        0,
        1,  // A: pri, tries, successful
        15,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_b", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);

  // Also check the other slot.
  SetMD(15,
        0,
        1,
        SV_UNV,
        0,
        0,  // A: pri, tries, success, slot_validity, RIs
        14,
        0,
        1,
        SV_OK,
        0,
        0,  // B: pri, tries, success, slot_validity, RIs
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  EXPECT_EQ(AVB_AB_FLOW_RESULT_OK_WITH_VERIFICATION_ERROR,
            avb_ab_flow(ops_.avb_ab_ops(),
                        requested_partitions,
                        AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR,
                        AVB_HASHTREE_ERROR_MODE_RESTART_AND_INVALIDATE,
                        &data));
  ExpMD(15,
        0,
        1,  // A: pri, tries, successful
        14,
        0,
        1,                           // B: pri, tries, successful
        MakeRollbackIndexes(0, 0));  // stored_rollback_indexes
  ASSERT_NE(nullptr, data);
  EXPECT_EQ("_a", std::string(data->ab_suffix));
  avb_slot_verify_data_free(data);
}

TEST_F(AvbABFlowTest, AvbtoolMetadataGeneratorEmptyFile) {
  AvbABData data;

  base::FilePath misc_path = testdir_.Append("misc.img");
  EXPECT_COMMAND(0,
                 "./avbtool set_ab_metadata"
                 " --misc_image %s"
                 " --slot_data 13:3:0:11:2:1",
                 misc_path.value().c_str());

  EXPECT_EQ(AVB_IO_RESULT_OK,
            ops_.avb_ab_ops()->read_ab_metadata(ops_.avb_ab_ops(), &data));
  EXPECT_EQ(13, data.slots[0].priority);
  EXPECT_EQ(3, data.slots[0].tries_remaining);
  EXPECT_EQ(0, data.slots[0].successful_boot);
  EXPECT_EQ(11, data.slots[1].priority);
  EXPECT_EQ(2, data.slots[1].tries_remaining);
  EXPECT_EQ(1, data.slots[1].successful_boot);
}

TEST_F(AvbABFlowTest, AvbtoolMetadataGeneratorExistingFile) {
  AvbABData data;
  size_t n;

  size_t misc_size = 1024 * 1024;
  base::FilePath misc_path = GenerateImage("misc.img", misc_size);
  EXPECT_COMMAND(0,
                 "./avbtool set_ab_metadata"
                 " --misc_image %s"
                 " --slot_data 12:2:1:10:5:0",
                 misc_path.value().c_str());

  EXPECT_EQ(AVB_IO_RESULT_OK,
            ops_.avb_ab_ops()->read_ab_metadata(ops_.avb_ab_ops(), &data));
  EXPECT_EQ(12, data.slots[0].priority);
  EXPECT_EQ(2, data.slots[0].tries_remaining);
  EXPECT_EQ(1, data.slots[0].successful_boot);
  EXPECT_EQ(10, data.slots[1].priority);
  EXPECT_EQ(5, data.slots[1].tries_remaining);
  EXPECT_EQ(0, data.slots[1].successful_boot);

  std::string misc_data;
  ASSERT_TRUE(base::ReadFileToString(misc_path, &misc_data));
  EXPECT_EQ(misc_size, misc_data.size());
  for (n = 0; n < 2048; n++) {
    ASSERT_EQ(uint8_t(misc_data[n]), uint8_t(n));
  }
  for (n = 2048 + 32; n < misc_data.size(); n++) {
    ASSERT_EQ(uint8_t(misc_data[n]), uint8_t(n));
  }
}

}  // namespace avb
