blob: 934149d58c1010571b4b54edc13668afff4e913d [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/cksum.h>
#include <string>
#include <libabr/abr_data.h>
#include <libabr/abr_util.h>
#include <libabr/libabr.h>
#include <zxtest/zxtest.h>
namespace {
extern "C" {
// A CRC32 implementation for the test environment.
uint32_t AbrCrc32(const void* buf, size_t buf_size) {
return crc32(0UL, reinterpret_cast<const uint8_t*>(buf), buf_size);
}
// AVBOps implementations which forward to a LibabrTest instance.
bool FakeReadAbrMetadata(void* context, size_t size, uint8_t* buffer);
bool FakeWriteAbrMetadata(void* context, const uint8_t* buffer, size_t size);
} // extern "C"
// Call this after messing with metadata (if you want the CRC to match).
void UpdateMetadataCRC(AbrData* metadata) {
metadata->crc32 = AbrHostToBigEndian(AbrCrc32(metadata, sizeof(*metadata) - sizeof(uint32_t)));
}
// Initializes metadata to a valid state where both slots are unbootable.
void InitializeMetadata(AbrData* metadata) {
memset(metadata, 0, sizeof(*metadata));
memcpy(metadata->magic, kAbrMagic, kAbrMagicLen);
metadata->version_major = kAbrMajorVersion;
metadata->version_minor = kAbrMinorVersion;
UpdateMetadataCRC(metadata);
}
// Checks that metadata is valid and normalized. These conditions should always be true after
// libabr has updated the metadata, even if previous metadata was invalid.
void ValidateMetadata(const AbrData& metadata) {
EXPECT_EQ(0, memcmp(metadata.magic, kAbrMagic, kAbrMagicLen));
EXPECT_EQ(AbrBigEndianToHost(metadata.crc32),
AbrCrc32(&metadata, sizeof(metadata) - sizeof(uint32_t)));
EXPECT_EQ(kAbrMajorVersion, metadata.version_major);
EXPECT_EQ(kAbrMinorVersion, metadata.version_minor);
for (auto slot_index : {kAbrSlotIndexA, kAbrSlotIndexB}) {
// If priority is zero, tries_remaining must also be zero.
EXPECT_TRUE(metadata.slot_data[slot_index].priority > 0 ||
metadata.slot_data[slot_index].tries_remaining == 0);
// If priority is zero, successful_boot must also be zero.
EXPECT_TRUE(metadata.slot_data[slot_index].priority > 0 ||
metadata.slot_data[slot_index].successful_boot == 0);
// If priority is not zero, tries_remaining and successful_boot must be consistent.
EXPECT_TRUE(metadata.slot_data[slot_index].priority == 0 ||
((metadata.slot_data[slot_index].tries_remaining > 0) !=
(metadata.slot_data[slot_index].successful_boot > 0)));
// Priority and tries_remaining must be in range.
EXPECT_LE(metadata.slot_data[slot_index].priority, kAbrMaxPriority);
EXPECT_LE(metadata.slot_data[slot_index].tries_remaining, kAbrMaxTriesRemaining);
}
}
struct FakeOps {
operator const AbrOps*() const { return &ops_; }
// AbrOps calls to |read_abr_metadata| forward here.
bool ReadMetadata(size_t size, uint8_t* buffer) {
read_metadata_count_++;
EXPECT_EQ(size, sizeof(AbrData));
if (size != sizeof(AbrData)) {
return false;
}
memcpy(buffer, &metadata_, sizeof(metadata_));
return read_metadata_result_;
}
// AbrOps calls to |write_abr_metadata| forward here.
bool WriteMetadata(const uint8_t* buffer, size_t size) {
write_metadata_count_++;
EXPECT_EQ(size, sizeof(AbrData));
if (size != sizeof(AbrData)) {
return false;
}
memcpy(&metadata_, buffer, sizeof(metadata_));
return write_metadata_result_;
}
// Set these to false in a test to induce I/O errors.
bool read_metadata_result_ = true;
bool write_metadata_result_ = true;
// These will be incremented on every AbrOps call from libabr.
int read_metadata_count_ = 0;
int write_metadata_count_ = 0;
// This will be used as the 'stored' metadata for all AbrOps callbacks.
AbrData metadata_{};
// This will be used as the AbrOps argument for libabr calls.
AbrOps ops_ = {this, FakeReadAbrMetadata, FakeWriteAbrMetadata};
};
FakeOps FakeOpsWithInitializedMetadata() {
FakeOps ops;
InitializeMetadata(&ops.metadata_);
return ops;
}
AbrSlotIndex OtherSlot(AbrSlotIndex slot_index) {
EXPECT_NE(kAbrSlotIndexR, slot_index);
return (slot_index == kAbrSlotIndexA) ? kAbrSlotIndexB : kAbrSlotIndexA;
}
// These callbacks forward to a FakeOps instance.
bool FakeReadAbrMetadata(void* context, size_t size, uint8_t* buffer) {
return reinterpret_cast<FakeOps*>(context)->ReadMetadata(size, buffer);
}
bool FakeWriteAbrMetadata(void* context, const uint8_t* buffer, size_t size) {
return reinterpret_cast<FakeOps*>(context)->WriteMetadata(buffer, size);
}
TEST(LibabrTest, GetBootSlotNotInitialized) {
FakeOps ops;
memset(&ops.metadata_, 0, sizeof(ops.metadata_));
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, true, nullptr));
ValidateMetadata(ops.metadata_);
}
void GetBootSlotActiveNotSuccessful(AbrSlotIndex slot_index) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
bool is_slot_marked_successful = true;
EXPECT_EQ(slot_index, AbrGetBootSlot(ops, true, &is_slot_marked_successful));
EXPECT_FALSE(is_slot_marked_successful);
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotActiveNotSuccessfulA) {
GetBootSlotActiveNotSuccessful(kAbrSlotIndexA);
}
TEST(LibabrTest, GetBootSlotActiveNotSuccessfulB) {
GetBootSlotActiveNotSuccessful(kAbrSlotIndexB);
}
void GetBootSlotActiveSuccessful(AbrSlotIndex slot_index) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
ASSERT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, slot_index));
bool is_slot_marked_successful = false;
EXPECT_EQ(slot_index, AbrGetBootSlot(ops, true, &is_slot_marked_successful));
EXPECT_TRUE(is_slot_marked_successful);
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotActiveSuccessfulA) { GetBootSlotActiveSuccessful(kAbrSlotIndexA); }
TEST(LibabrTest, GetBootSlotActiveSuccessfulB) { GetBootSlotActiveSuccessful(kAbrSlotIndexB); }
TEST(LibabrTest, GetBootSlotNoBootableSlot) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrSlotIndexR, AbrGetBootSlot(ops, false, nullptr));
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotNullReadOp) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.ops_.read_abr_metadata = nullptr;
// The expectation is a fallback to recovery.
EXPECT_EQ(kAbrSlotIndexR, AbrGetBootSlot(ops, true, nullptr));
}
TEST(LibabrTest, GetBootSlotNullWriteOpNoUpdate) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.ops_.write_abr_metadata = nullptr;
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, false, nullptr));
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotNullWriteOpUpdate) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.ops_.write_abr_metadata = nullptr;
// The expectation is to ignore the write error.
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, true, nullptr));
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotReadIOError) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.read_metadata_result_ = false;
// The expectation is a fallback to recovery.
EXPECT_EQ(kAbrSlotIndexR, AbrGetBootSlot(ops, true, nullptr));
}
TEST(LibabrTest, GetBootSlotWriteIOErrorNoUpdate) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.write_metadata_result_ = false;
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, false, nullptr));
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotWriteIOErrorUpdate) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.write_metadata_result_ = false;
// The expectation is to ignore the write error.
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, true, nullptr));
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotInvalidMetadataBadMagic) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexB));
ops.metadata_.magic[0] = 'a';
UpdateMetadataCRC(&ops.metadata_);
// The expectation is that metadata is reinitialized, with A active.
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, true, nullptr));
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotInvalidMetadataBadCRC) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexB));
ops.metadata_.crc32 = 0;
// The expectation is that metadata is reinitialized, with A active.
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, true, nullptr));
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotInvalidMetadataUnsupportedVersion) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexB));
ops.metadata_.version_major = 27;
UpdateMetadataCRC(&ops.metadata_);
// The expectation is a fallback to recovery without clobbering metadata.
EXPECT_EQ(kAbrSlotIndexR, AbrGetBootSlot(ops, true, nullptr));
EXPECT_EQ(ops.metadata_.version_major, 27);
}
TEST(LibabrTest, GetBootSlotInvalidMetadataLittleEndianCRC) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ops.metadata_.crc32 = AbrCrc32(&ops.metadata_, sizeof(ops.metadata_) - sizeof(uint32_t));
// The expectation is that metadata is reinitialized, with A active.
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, true, nullptr));
ValidateMetadata(ops.metadata_);
}
void GetBootSlotNormalizeUnexpectedTries(AbrSlotIndex slot_index) {
FakeOps ops = FakeOpsWithInitializedMetadata();
// Set the metadata to a state where priority is zero, but tries remain.
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
ops.metadata_.slot_data[slot_index].priority = 0;
UpdateMetadataCRC(&ops.metadata_);
EXPECT_EQ(kAbrSlotIndexR, AbrGetBootSlot(ops, true, nullptr));
// The expectation is that the metadata has been normalized and updated.
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotNormalizeUnexpectedTriesA) {
GetBootSlotNormalizeUnexpectedTries(kAbrSlotIndexA);
}
TEST(LibabrTest, GetBootSlotNormalizeUnexpectedTriesB) {
GetBootSlotNormalizeUnexpectedTries(kAbrSlotIndexB);
}
void GetBootSlotNormalizeUnexpectedSuccessMark(AbrSlotIndex slot_index) {
FakeOps ops = FakeOpsWithInitializedMetadata();
// Set the metadata to a state where priority is zero, but marked successful.
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
ASSERT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, slot_index));
ops.metadata_.slot_data[slot_index].priority = 0;
UpdateMetadataCRC(&ops.metadata_);
EXPECT_EQ(kAbrSlotIndexR, AbrGetBootSlot(ops, true, nullptr));
// The expectation is that the metadata has been normalized and updated.
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotNormalizeUnexpectedSuccessMarkA) {
GetBootSlotNormalizeUnexpectedSuccessMark(kAbrSlotIndexA);
}
TEST(LibabrTest, GetBootSlotNormalizeUnexpectedSuccessMarkB) {
GetBootSlotNormalizeUnexpectedSuccessMark(kAbrSlotIndexB);
}
void GetBootSlotNormalizeTriesExhausted(AbrSlotIndex slot_index) {
FakeOps ops = FakeOpsWithInitializedMetadata();
// Set the metadata to a state where tries are exhausted and no successful mark.
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
ops.metadata_.slot_data[slot_index].tries_remaining = 0;
UpdateMetadataCRC(&ops.metadata_);
EXPECT_EQ(kAbrSlotIndexR, AbrGetBootSlot(ops, true, nullptr));
// The expectation is that the metadata has been normalized and updated.
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotNormalizeTriesExhaustedA) {
GetBootSlotNormalizeTriesExhausted(kAbrSlotIndexA);
}
TEST(LibabrTest, GetBootSlotNormalizeTriesExhaustedB) {
GetBootSlotNormalizeTriesExhausted(kAbrSlotIndexB);
}
void GetBootSlotNormalizeSuccessfulWithUnexpectedTries(AbrSlotIndex slot_index) {
FakeOps ops = FakeOpsWithInitializedMetadata();
// Set the metadata to a state where tries remain alongside a successful mark.
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
ASSERT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, slot_index));
ops.metadata_.slot_data[slot_index].tries_remaining = 3;
UpdateMetadataCRC(&ops.metadata_);
// Expect that the slot is reset to newly active state.
bool is_slot_marked_successful = true;
EXPECT_EQ(slot_index, AbrGetBootSlot(ops, true, &is_slot_marked_successful));
EXPECT_FALSE(is_slot_marked_successful);
// The expectation is that the metadata has been normalized and updated.
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, GetBootSlotNormalizeSuccessfulWithUnexpectedTriesA) {
GetBootSlotNormalizeSuccessfulWithUnexpectedTries(kAbrSlotIndexA);
}
TEST(LibabrTest, GetBootSlotNormalizeSuccessfulWithUnexpectedTriesB) {
GetBootSlotNormalizeSuccessfulWithUnexpectedTries(kAbrSlotIndexB);
}
void GetBootSlotNormalizePriorityOutOfRange(AbrSlotIndex slot_index) {
FakeOps ops = FakeOpsWithInitializedMetadata();
// Set the metadata to an active state where priority is higher than max.
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
ops.metadata_.slot_data[slot_index].priority = kAbrMaxPriority + 1;
UpdateMetadataCRC(&ops.metadata_);
EXPECT_EQ(slot_index, AbrGetBootSlot(ops, true, nullptr));
// The expectation is that the metadata has been normalized and updated.
ValidateMetadata(ops.metadata_);
// When at max, should not change.
ops.metadata_.slot_data[slot_index].priority = kAbrMaxPriority;
UpdateMetadataCRC(&ops.metadata_);
AbrGetBootSlot(ops, true, nullptr);
EXPECT_EQ(ops.metadata_.slot_data[slot_index].priority, kAbrMaxPriority);
}
TEST(LibabrTest, GetBootSlotNormalizePriorityOutOfRangeA) {
GetBootSlotNormalizePriorityOutOfRange(kAbrSlotIndexA);
}
TEST(LibabrTest, GetBootSlotNormalizePriorityOutOfRangeB) {
GetBootSlotNormalizePriorityOutOfRange(kAbrSlotIndexB);
}
void GetBootSlotNormalizeTriesOutOfRange(AbrSlotIndex slot_index) {
FakeOps ops = FakeOpsWithInitializedMetadata();
// Set the metadata to an active state where tries_remaining is higher than max.
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
ops.metadata_.slot_data[slot_index].tries_remaining = kAbrMaxTriesRemaining + 1;
UpdateMetadataCRC(&ops.metadata_);
EXPECT_EQ(slot_index, AbrGetBootSlot(ops, true, nullptr));
// The expectation is that the metadata has been normalized first and then the usual decrement.
ValidateMetadata(ops.metadata_);
EXPECT_EQ(ops.metadata_.slot_data[slot_index].tries_remaining, kAbrMaxTriesRemaining - 1);
// When at max, should not change except for the usual decrement.
ops.metadata_.slot_data[slot_index].tries_remaining = kAbrMaxTriesRemaining;
UpdateMetadataCRC(&ops.metadata_);
AbrGetBootSlot(ops, true, nullptr);
EXPECT_EQ(ops.metadata_.slot_data[slot_index].tries_remaining, kAbrMaxTriesRemaining - 1);
}
TEST(LibabrTest, GetBootSlotNormalizeTriesOutOfRangeA) {
GetBootSlotNormalizeTriesOutOfRange(kAbrSlotIndexA);
}
TEST(LibabrTest, GetBootSlotNormalizeTriesOutOfRangeB) {
GetBootSlotNormalizeTriesOutOfRange(kAbrSlotIndexB);
}
TEST(LibabrTest, GetBootSlotOneShotRecovery) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexB));
ASSERT_EQ(kAbrResultOk, AbrSetOneShotRecovery(ops, true));
EXPECT_EQ(kAbrSlotIndexR, AbrGetBootSlot(ops, true, nullptr));
ValidateMetadata(ops.metadata_);
// The setting should be automatically reset.
EXPECT_FALSE(ops.metadata_.one_shot_recovery_boot);
}
TEST(LibabrTest, GetBootSlotOneShotRecoveryNoUpdate) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexB));
ASSERT_EQ(kAbrResultOk, AbrSetOneShotRecovery(ops, true));
EXPECT_EQ(kAbrSlotIndexB, AbrGetBootSlot(ops, false, nullptr));
ValidateMetadata(ops.metadata_);
// The setting was ignored so should persist.
EXPECT_TRUE(ops.metadata_.one_shot_recovery_boot);
}
TEST(LibabrTest, GetBootSlotUpdateTryCount) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexB));
ops.metadata_.slot_data[kAbrSlotIndexB].tries_remaining = 3;
UpdateMetadataCRC(&ops.metadata_);
EXPECT_EQ(kAbrSlotIndexB, AbrGetBootSlot(ops, true, nullptr));
ValidateMetadata(ops.metadata_);
// Should be decremented by exactly one: 3 -> 2.
EXPECT_EQ(2, ops.metadata_.slot_data[kAbrSlotIndexB].tries_remaining);
}
TEST(LibabrTest, GetBootSlotNoUpdates) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexB));
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrSlotIndexB, AbrGetBootSlot(ops, false, nullptr));
ValidateMetadata(ops.metadata_);
EXPECT_EQ(0, ops.write_metadata_count_);
}
TEST(LibabrTest, GetBootSlotNoUpdatesFromNotInit) {
FakeOps ops;
memset(&ops.metadata_, 0, sizeof(ops.metadata_));
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, false, nullptr));
EXPECT_EQ(0, ops.write_metadata_count_);
}
TEST(LibabrTest, GetBootSlotNoUpdatesFromNotNormalized) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexB));
ops.metadata_.slot_data[kAbrSlotIndexB].priority = 0;
UpdateMetadataCRC(&ops.metadata_);
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrSlotIndexR, AbrGetBootSlot(ops, false, nullptr));
EXPECT_EQ(0, ops.write_metadata_count_);
}
TEST(LibabrTest, GetBootSlotNoExtraneousReads) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrSlotIndexR, AbrGetBootSlot(ops, false, nullptr));
EXPECT_EQ(1, ops.read_metadata_count_);
}
TEST(LibabrTest, GetBootSlotNoExtraneousWrites) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ASSERT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, kAbrSlotIndexA));
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, true, nullptr));
EXPECT_EQ(0, ops.write_metadata_count_);
}
TEST(LibabrTest, GetBootSlotNoExtraneousWritesOneUpdate) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrSlotIndexA, AbrGetBootSlot(ops, true, nullptr));
// Expecting an update because of the tries_remaining decrement, but should be just one.
EXPECT_EQ(1, ops.write_metadata_count_);
}
TEST(LibabrTest, GetSlotSuffix) {
EXPECT_EQ(std::string("_a"), AbrGetSlotSuffix(kAbrSlotIndexA));
EXPECT_EQ(std::string("_b"), AbrGetSlotSuffix(kAbrSlotIndexB));
EXPECT_EQ(std::string("_r"), AbrGetSlotSuffix(kAbrSlotIndexR));
}
TEST(LibabrTest, GetSlotSuffixInvalidIndex) {
EXPECT_EQ(std::string(""), AbrGetSlotSuffix((AbrSlotIndex)-1));
}
void MarkSlotActive(AbrSlotIndex slot_index) {
AbrSlotIndex other_slot_index = OtherSlot(slot_index);
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
EXPECT_GT(ops.metadata_.slot_data[slot_index].priority, 0);
EXPECT_GT(ops.metadata_.slot_data[slot_index].tries_remaining, 0);
EXPECT_EQ(ops.metadata_.slot_data[slot_index].successful_boot, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].priority, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].tries_remaining, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].successful_boot, 0);
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, MarkSlotActiveA) { MarkSlotActive(kAbrSlotIndexA); }
TEST(LibabrTest, MarkSlotActiveB) { MarkSlotActive(kAbrSlotIndexB); }
void MarkSlotActiveOverOther(AbrSlotIndex slot_index) {
AbrSlotIndex other_slot_index = OtherSlot(slot_index);
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, other_slot_index));
ASSERT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, other_slot_index));
EXPECT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
EXPECT_GT(ops.metadata_.slot_data[slot_index].priority,
ops.metadata_.slot_data[other_slot_index].priority);
EXPECT_GT(ops.metadata_.slot_data[slot_index].tries_remaining, 0);
EXPECT_EQ(ops.metadata_.slot_data[slot_index].successful_boot, 0);
EXPECT_GT(ops.metadata_.slot_data[other_slot_index].priority, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].tries_remaining, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].successful_boot, 1);
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, MarkSlotActiveOverOtherA) { MarkSlotActiveOverOther(kAbrSlotIndexA); }
TEST(LibabrTest, MarkSlotActiveOverOtherB) { MarkSlotActiveOverOther(kAbrSlotIndexB); }
TEST(LibabrTest, MarkSlotActiveR) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultErrorInvalidData, AbrMarkSlotActive(ops, kAbrSlotIndexR));
}
TEST(LibabrTest, MarkSlotActiveInvalidIndex) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultErrorInvalidData, AbrMarkSlotActive(ops, (AbrSlotIndex)-1));
}
TEST(LibabrTest, MarkSlotActiveReadFailure) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ops.read_metadata_result_ = false;
EXPECT_EQ(kAbrResultErrorIo, AbrMarkSlotActive(ops, kAbrSlotIndexA));
}
TEST(LibabrTest, MarkSlotActiveWriteFailure) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ops.write_metadata_result_ = false;
EXPECT_EQ(kAbrResultErrorIo, AbrMarkSlotActive(ops, kAbrSlotIndexA));
}
TEST(LibabrTest, MarkSlotActiveNoExtraneousReads) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
EXPECT_EQ(1, ops.read_metadata_count_);
}
TEST(LibabrTest, MarkSlotActiveNoExtraneousWrites) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
EXPECT_EQ(1, ops.write_metadata_count_);
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
EXPECT_EQ(0, ops.write_metadata_count_);
}
void MarkSlotUnbootable(AbrSlotIndex slot_index) {
AbrSlotIndex other_slot_index = OtherSlot(slot_index);
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
ASSERT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, slot_index));
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, other_slot_index));
ASSERT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, other_slot_index));
EXPECT_EQ(kAbrResultOk, AbrMarkSlotUnbootable(ops, slot_index));
EXPECT_EQ(ops.metadata_.slot_data[slot_index].priority, 0);
EXPECT_EQ(ops.metadata_.slot_data[slot_index].tries_remaining, 0);
EXPECT_EQ(ops.metadata_.slot_data[slot_index].successful_boot, 0);
EXPECT_GT(ops.metadata_.slot_data[other_slot_index].priority, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].tries_remaining, 0);
EXPECT_GT(ops.metadata_.slot_data[other_slot_index].successful_boot, 0);
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, MarkSlotUnbootableA) { MarkSlotUnbootable(kAbrSlotIndexA); }
TEST(LibabrTest, MarkSlotUnbootableB) { MarkSlotUnbootable(kAbrSlotIndexB); }
TEST(LibabrTest, MarkSlotUnbootableR) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultErrorInvalidData, AbrMarkSlotUnbootable(ops, kAbrSlotIndexR));
}
TEST(LibabrTest, MarkSlotUnbootableInvalidIndex) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultErrorInvalidData, AbrMarkSlotUnbootable(ops, (AbrSlotIndex)-1));
}
TEST(LibabrTest, MarkSlotUnbootableReadFailure) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.read_metadata_result_ = false;
EXPECT_EQ(kAbrResultErrorIo, AbrMarkSlotUnbootable(ops, kAbrSlotIndexA));
}
TEST(LibabrTest, MarkSlotUnbootableWriteFailure) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.write_metadata_result_ = false;
EXPECT_EQ(kAbrResultErrorIo, AbrMarkSlotUnbootable(ops, kAbrSlotIndexA));
}
TEST(LibabrTest, MarkSlotUnbootableNoExtraneousReads) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.read_metadata_count_ = 0;
EXPECT_EQ(kAbrResultOk, AbrMarkSlotUnbootable(ops, kAbrSlotIndexA));
EXPECT_EQ(1, ops.read_metadata_count_);
}
TEST(LibabrTest, MarkSlotUnbootableNoExtraneousWrites) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrResultOk, AbrMarkSlotUnbootable(ops, kAbrSlotIndexA));
EXPECT_EQ(1, ops.write_metadata_count_);
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrResultOk, AbrMarkSlotUnbootable(ops, kAbrSlotIndexA));
EXPECT_EQ(0, ops.write_metadata_count_);
}
void MarkSlotSuccessful(AbrSlotIndex slot_index) {
AbrSlotIndex other_slot_index = OtherSlot(slot_index);
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
EXPECT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, slot_index));
EXPECT_GT(ops.metadata_.slot_data[slot_index].priority, 0);
EXPECT_EQ(ops.metadata_.slot_data[slot_index].tries_remaining, 0);
EXPECT_GT(ops.metadata_.slot_data[slot_index].successful_boot, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].priority, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].tries_remaining, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].successful_boot, 0);
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, MarkSlotSuccessfulA) { MarkSlotSuccessful(kAbrSlotIndexA); }
TEST(LibabrTest, MarkSlotSuccessfulB) { MarkSlotSuccessful(kAbrSlotIndexB); }
TEST(LibabrTest, MarkSlotSuccessfulR) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultErrorInvalidData, AbrMarkSlotSuccessful(ops, kAbrSlotIndexR));
}
TEST(LibabrTest, MarkSlotSuccessfulInvalidIndex) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultErrorInvalidData, AbrMarkSlotSuccessful(ops, (AbrSlotIndex)-1));
}
TEST(LibabrTest, MarkSlotSuccessfulUnbootable) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultErrorInvalidData, AbrMarkSlotSuccessful(ops, kAbrSlotIndexA));
}
TEST(LibabrTest, MarkSlotSuccessfulReadFailure) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.read_metadata_result_ = false;
EXPECT_EQ(kAbrResultErrorIo, AbrMarkSlotSuccessful(ops, kAbrSlotIndexA));
}
TEST(LibabrTest, MarkSlotSuccessfulWriteFailure) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.write_metadata_result_ = false;
EXPECT_EQ(kAbrResultErrorIo, AbrMarkSlotSuccessful(ops, kAbrSlotIndexA));
}
TEST(LibabrTest, MarkSlotSuccessfulNoExtraneousReads) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.read_metadata_count_ = 0;
EXPECT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, kAbrSlotIndexA));
EXPECT_EQ(1, ops.read_metadata_count_);
}
TEST(LibabrTest, MarkSlotSuccessfulNoExtraneousWrites) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, kAbrSlotIndexA));
EXPECT_EQ(1, ops.write_metadata_count_);
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, kAbrSlotIndexA));
EXPECT_EQ(0, ops.write_metadata_count_);
}
void MarkSlotSuccessfulWithOtherSuccessful(AbrSlotIndex slot_index) {
AbrSlotIndex other_slot_index = OtherSlot(slot_index);
FakeOps ops = FakeOpsWithInitializedMetadata();
// First set up the other slot as successful.
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, other_slot_index));
ASSERT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, other_slot_index));
EXPECT_GT(ops.metadata_.slot_data[other_slot_index].priority, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].tries_remaining, 0);
EXPECT_NE(ops.metadata_.slot_data[other_slot_index].successful_boot, 0);
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
EXPECT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, slot_index));
EXPECT_GT(ops.metadata_.slot_data[slot_index].priority, 0);
EXPECT_EQ(ops.metadata_.slot_data[slot_index].tries_remaining, 0);
EXPECT_NE(ops.metadata_.slot_data[slot_index].successful_boot, 0);
// Expect that the other slot has been marked not successful.
EXPECT_NE(ops.metadata_.slot_data[other_slot_index].priority, 0);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].tries_remaining, kAbrMaxTriesRemaining);
EXPECT_EQ(ops.metadata_.slot_data[other_slot_index].successful_boot, 0);
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, MarkSlotSuccessfulWithOtherSuccessfulA) {
MarkSlotSuccessfulWithOtherSuccessful(kAbrSlotIndexA);
}
TEST(LibabrTest, MarkSlotSuccessfulWithOtherSuccessfulB) {
MarkSlotSuccessfulWithOtherSuccessful(kAbrSlotIndexB);
}
void GetSlotInfo(AbrSlotIndex slot_index) {
AbrSlotIndex other_slot_index = OtherSlot(slot_index);
FakeOps ops = FakeOpsWithInitializedMetadata();
AbrSlotInfo info;
ASSERT_EQ(kAbrResultOk, AbrGetSlotInfo(ops, slot_index, &info));
EXPECT_FALSE(info.is_bootable);
EXPECT_FALSE(info.is_active);
EXPECT_FALSE(info.is_marked_successful);
EXPECT_EQ(info.num_tries_remaining, 0);
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, slot_index));
ASSERT_EQ(kAbrResultOk, AbrGetSlotInfo(ops, slot_index, &info));
EXPECT_TRUE(info.is_bootable);
EXPECT_TRUE(info.is_active);
EXPECT_FALSE(info.is_marked_successful);
EXPECT_GT(info.num_tries_remaining, 0);
ASSERT_EQ(kAbrResultOk, AbrMarkSlotSuccessful(ops, slot_index));
ASSERT_EQ(kAbrResultOk, AbrGetSlotInfo(ops, slot_index, &info));
EXPECT_TRUE(info.is_bootable);
EXPECT_TRUE(info.is_active);
EXPECT_TRUE(info.is_marked_successful);
EXPECT_EQ(info.num_tries_remaining, 0);
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, other_slot_index));
ASSERT_EQ(kAbrResultOk, AbrGetSlotInfo(ops, slot_index, &info));
EXPECT_TRUE(info.is_bootable);
EXPECT_FALSE(info.is_active);
EXPECT_TRUE(info.is_marked_successful);
EXPECT_EQ(info.num_tries_remaining, 0);
ASSERT_EQ(kAbrResultOk, AbrMarkSlotUnbootable(ops, slot_index));
ASSERT_EQ(kAbrResultOk, AbrGetSlotInfo(ops, slot_index, &info));
EXPECT_FALSE(info.is_bootable);
EXPECT_FALSE(info.is_active);
EXPECT_FALSE(info.is_marked_successful);
EXPECT_EQ(info.num_tries_remaining, 0);
}
TEST(LibabrTest, GetSlotInfoA) { GetSlotInfo(kAbrSlotIndexA); }
TEST(LibabrTest, GetSlotInfoB) { GetSlotInfo(kAbrSlotIndexB); }
TEST(LibabrTest, GetSlotInfoR) {
FakeOps ops = FakeOpsWithInitializedMetadata();
AbrSlotInfo info;
ASSERT_EQ(kAbrResultOk, AbrGetSlotInfo(ops, kAbrSlotIndexR, &info));
EXPECT_TRUE(info.is_bootable);
EXPECT_TRUE(info.is_active);
EXPECT_TRUE(info.is_marked_successful);
EXPECT_EQ(info.num_tries_remaining, 0);
// When any other slot is bootable, R is not active.
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexB));
ASSERT_EQ(kAbrResultOk, AbrGetSlotInfo(ops, kAbrSlotIndexR, &info));
EXPECT_TRUE(info.is_bootable);
EXPECT_FALSE(info.is_active);
EXPECT_TRUE(info.is_marked_successful);
EXPECT_EQ(info.num_tries_remaining, 0);
ASSERT_EQ(kAbrResultOk, AbrMarkSlotUnbootable(ops, kAbrSlotIndexB));
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexA));
ASSERT_EQ(kAbrResultOk, AbrGetSlotInfo(ops, kAbrSlotIndexR, &info));
EXPECT_TRUE(info.is_bootable);
EXPECT_FALSE(info.is_active);
EXPECT_TRUE(info.is_marked_successful);
EXPECT_EQ(info.num_tries_remaining, 0);
ASSERT_EQ(kAbrResultOk, AbrMarkSlotActive(ops, kAbrSlotIndexB));
EXPECT_TRUE(info.is_bootable);
EXPECT_FALSE(info.is_active);
EXPECT_TRUE(info.is_marked_successful);
EXPECT_EQ(info.num_tries_remaining, 0);
}
TEST(LibabrTest, GetSlotInfoReadFailure) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ops.read_metadata_result_ = false;
AbrSlotInfo info;
EXPECT_EQ(kAbrResultErrorIo, AbrGetSlotInfo(ops, kAbrSlotIndexB, &info));
}
TEST(LibabrTest, GetSlotInfoNoExtraneousReads) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ops.read_metadata_count_ = 0;
AbrSlotInfo info;
EXPECT_EQ(kAbrResultOk, AbrGetSlotInfo(ops, kAbrSlotIndexB, &info));
EXPECT_EQ(1, ops.read_metadata_count_);
}
TEST(LibabrTest, GetSlotInfoNoWrites) {
FakeOps ops = FakeOpsWithInitializedMetadata();
AbrSlotInfo info;
EXPECT_EQ(kAbrResultOk, AbrGetSlotInfo(ops, kAbrSlotIndexB, &info));
EXPECT_EQ(0, ops.write_metadata_count_);
}
TEST(LibabrTest, SetOneShotRecovery) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultOk, AbrSetOneShotRecovery(ops, true));
EXPECT_EQ(1, ops.metadata_.one_shot_recovery_boot);
ValidateMetadata(ops.metadata_);
EXPECT_EQ(kAbrResultOk, AbrSetOneShotRecovery(ops, false));
EXPECT_EQ(0, ops.metadata_.one_shot_recovery_boot);
ValidateMetadata(ops.metadata_);
}
TEST(LibabrTest, SetOneShotRecoveryReadFailure) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ops.read_metadata_result_ = false;
EXPECT_EQ(kAbrResultErrorIo, AbrSetOneShotRecovery(ops, true));
}
TEST(LibabrTest, SetOneShotRecoveryWriteFailure) {
FakeOps ops = FakeOpsWithInitializedMetadata();
ops.write_metadata_result_ = false;
EXPECT_EQ(kAbrResultErrorIo, AbrSetOneShotRecovery(ops, true));
}
TEST(LibabrTest, SetOneShotRecoveryNoExtraneousReads) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultOk, AbrSetOneShotRecovery(ops, true));
EXPECT_EQ(1, ops.read_metadata_count_);
}
TEST(LibabrTest, SetOneShotRecoveryNoExtraneousWrites) {
FakeOps ops = FakeOpsWithInitializedMetadata();
EXPECT_EQ(kAbrResultOk, AbrSetOneShotRecovery(ops, true));
EXPECT_EQ(1, ops.write_metadata_count_);
ops.write_metadata_count_ = 0;
EXPECT_EQ(kAbrResultOk, AbrSetOneShotRecovery(ops, true));
EXPECT_EQ(0, ops.write_metadata_count_);
}
} // namespace