520 lines
25 KiB
C++
520 lines
25 KiB
C++
#include "configuration/adapter_configuration.h"
|
|
#include "configuration/configuration_service.h"
|
|
#include "configuration/configuration_storage.h"
|
|
#include "platform/pico/pico_configuration_storage.h"
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
|
|
namespace {
|
|
|
|
constexpr size_t kSectorSize = 4096;
|
|
constexpr size_t kPageSize = 256;
|
|
|
|
struct FakeFlash {
|
|
uint8_t bytes[CONFIGURATION_STORAGE_COPY_COUNT][kSectorSize];
|
|
int read_count = 0;
|
|
int erase_count = 0;
|
|
int program_count = 0;
|
|
bool fail_program = false;
|
|
|
|
FakeFlash() { memset(bytes, 0xff, sizeof(bytes)); }
|
|
};
|
|
|
|
FakeFlash g_flash;
|
|
bool g_reserve_recovery_during_program = false;
|
|
|
|
void require(bool condition, const char* message) {
|
|
if (!condition) {
|
|
std::cerr << message << '\n';
|
|
std::exit(1);
|
|
}
|
|
}
|
|
|
|
bool fake_read(void* context, uint8_t copy, size_t offset,
|
|
uint8_t* output, size_t size) {
|
|
auto* flash = static_cast<FakeFlash*>(context);
|
|
if (copy >= CONFIGURATION_STORAGE_COPY_COUNT ||
|
|
offset + size > kSectorSize) {
|
|
return false;
|
|
}
|
|
memcpy(output, &flash->bytes[copy][offset], size);
|
|
++flash->read_count;
|
|
return true;
|
|
}
|
|
|
|
bool fake_erase(void* context, uint8_t copy) {
|
|
auto* flash = static_cast<FakeFlash*>(context);
|
|
if (copy >= CONFIGURATION_STORAGE_COPY_COUNT) {
|
|
return false;
|
|
}
|
|
memset(flash->bytes[copy], 0xff, kSectorSize);
|
|
++flash->erase_count;
|
|
return true;
|
|
}
|
|
|
|
bool fake_program(void* context, uint8_t copy, size_t offset,
|
|
const uint8_t* data, size_t size) {
|
|
auto* flash = static_cast<FakeFlash*>(context);
|
|
if (copy >= CONFIGURATION_STORAGE_COPY_COUNT || size != kPageSize ||
|
|
offset + size > kSectorSize || flash->fail_program) {
|
|
return false;
|
|
}
|
|
if (g_reserve_recovery_during_program) {
|
|
g_reserve_recovery_during_program = false;
|
|
configuration_service_reserve_for_recovery();
|
|
}
|
|
for (size_t index = 0; index < size; ++index) {
|
|
flash->bytes[copy][offset + index] &= data[index];
|
|
}
|
|
++flash->program_count;
|
|
return true;
|
|
}
|
|
|
|
ConfigurationStorageIo fake_io() {
|
|
return {
|
|
&g_flash,
|
|
kSectorSize,
|
|
kPageSize,
|
|
fake_read,
|
|
fake_erase,
|
|
fake_program,
|
|
};
|
|
}
|
|
|
|
void require_mode_status(uint32_t transaction_id,
|
|
ConfigurationTransactionStatus expected,
|
|
const char* message) {
|
|
ConfigurationTransactionStatus actual =
|
|
ConfigurationTransactionStatus::kIdle;
|
|
require(configuration_service_mode_transaction_status(transaction_id,
|
|
&actual) &&
|
|
actual == expected,
|
|
message);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
ConfigurationStorageIo pico_configuration_storage_io() {
|
|
return fake_io();
|
|
}
|
|
|
|
namespace {
|
|
|
|
void seed_legacy_configuration() {
|
|
ConfigurationStorage seed;
|
|
const uint8_t legacy[] = {90, 0, 0, 0};
|
|
require(seed.initialize(fake_io()), "legacy seed storage init failed");
|
|
require(seed.commit(ADAPTER_CONFIGURATION_LEGACY_SCHEMA_VERSION,
|
|
legacy, sizeof(legacy)) ==
|
|
ConfigurationStorageResult::kOk,
|
|
"legacy seed commit failed");
|
|
}
|
|
|
|
void test_service_lifecycle_and_mutations() {
|
|
seed_legacy_configuration();
|
|
const int programs_after_seed = g_flash.program_count;
|
|
const int erases_after_seed = g_flash.erase_count;
|
|
|
|
configuration_service_prepare();
|
|
configuration_service_initialize_pre_usb();
|
|
|
|
ConfigurationServiceSnapshot snapshot{};
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.state == ConfigurationServiceState::kReady &&
|
|
snapshot.configuration.pairing_window_seconds == 90 &&
|
|
snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kAuto,
|
|
"Core 0 did not decode and publish the v1 configuration");
|
|
require(g_flash.program_count == programs_after_seed &&
|
|
g_flash.erase_count == erases_after_seed,
|
|
"pre-USB initialization wrote flash");
|
|
|
|
const AdapterModeAvailability implemented{true, true, true, true};
|
|
require(configuration_service_set_mode(
|
|
1, AdapterRequestedMode::kSwitch, implemented) ==
|
|
ConfigurationTransactionStatus::kBusy,
|
|
"host mutation was accepted before storage-core adoption");
|
|
|
|
const int reads_before_adoption = g_flash.read_count;
|
|
configuration_service_initialize_on_storage_core();
|
|
require(g_flash.read_count == reads_before_adoption &&
|
|
g_flash.program_count == programs_after_seed &&
|
|
g_flash.erase_count == erases_after_seed,
|
|
"Core 1 reread or wrote instead of adopting Core 0 state");
|
|
require(configuration_service_set_mode(
|
|
2, AdapterRequestedMode::kSwitch, implemented) ==
|
|
ConfigurationTransactionStatus::kBusy,
|
|
"host mutation displaced the pending v1 migration");
|
|
|
|
configuration_service_task_on_storage_core(0);
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.state == ConfigurationServiceState::kReady &&
|
|
snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kAuto &&
|
|
snapshot.transaction.status ==
|
|
ConfigurationTransactionStatus::kIdle,
|
|
"v1 migration changed configuration or host transaction state");
|
|
|
|
ConfigurationStorage after_migration;
|
|
require(after_migration.initialize(fake_io()) &&
|
|
after_migration.snapshot().valid &&
|
|
after_migration.snapshot().schema_version ==
|
|
ADAPTER_CONFIGURATION_SCHEMA_VERSION &&
|
|
after_migration.snapshot().payload_size ==
|
|
ADAPTER_CONFIGURATION_ENCODED_SIZE,
|
|
"power cycle did not observe the migrated v2 record");
|
|
AdapterConfiguration migrated{};
|
|
require(adapter_configuration_decode(
|
|
after_migration.snapshot().schema_version,
|
|
after_migration.snapshot().payload,
|
|
after_migration.snapshot().payload_size, &migrated) &&
|
|
migrated.pairing_window_seconds == 90 &&
|
|
migrated.requested_mode == AdapterRequestedMode::kAuto,
|
|
"migrated v2 bytes did not preserve v1 configuration");
|
|
|
|
require(configuration_service_set_mode(
|
|
10, AdapterRequestedMode::kSwitch, implemented) ==
|
|
ConfigurationTransactionStatus::kPending,
|
|
"host mode transaction was not queued");
|
|
require_mode_status(10, ConfigurationTransactionStatus::kPending,
|
|
"host mode correlation was not pending");
|
|
require(configuration_service_set_mode_internal(
|
|
0x80000001u, AdapterRequestedMode::kXInput, implemented) ==
|
|
ConfigurationTransactionStatus::kBusy,
|
|
"internal mode displaced a pending host mode");
|
|
configuration_service_task_on_storage_core(999);
|
|
require_mode_status(10, ConfigurationTransactionStatus::kPending,
|
|
"write-rate guard committed host mode too early");
|
|
configuration_service_task_on_storage_core(1000);
|
|
require_mode_status(10, ConfigurationTransactionStatus::kCommitted,
|
|
"host mode transaction did not commit");
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.configuration.pairing_window_seconds == 90 &&
|
|
snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kSwitch &&
|
|
snapshot.transaction.transaction_id == 10 &&
|
|
snapshot.transaction.status ==
|
|
ConfigurationTransactionStatus::kCommitted,
|
|
"mode-only commit changed pairing state or host status");
|
|
|
|
const int programs_before_rejections = g_flash.program_count;
|
|
const AdapterModeAvailability unavailable{true, true, false, false};
|
|
require(configuration_service_set_mode(
|
|
11, AdapterRequestedMode::kDInput, unavailable) ==
|
|
ConfigurationTransactionStatus::kUnsupportedSchema &&
|
|
configuration_service_set_mode(
|
|
11, static_cast<AdapterRequestedMode>(5), implemented) ==
|
|
ConfigurationTransactionStatus::kMalformed &&
|
|
configuration_service_set_mode(
|
|
0x80000011u, AdapterRequestedMode::kXInput,
|
|
implemented) ==
|
|
ConfigurationTransactionStatus::kMalformed &&
|
|
configuration_service_begin(
|
|
0x80000012u, ADAPTER_CONFIGURATION_SCHEMA_VERSION,
|
|
ADAPTER_CONFIGURATION_ENCODED_SIZE, 0) ==
|
|
ConfigurationTransactionStatus::kMalformed &&
|
|
configuration_service_reset(0x80000013u) ==
|
|
ConfigurationTransactionStatus::kMalformed,
|
|
"unavailable, invalid, or internal-range host request was accepted");
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kSwitch &&
|
|
snapshot.transaction.transaction_id == 10 &&
|
|
g_flash.program_count == programs_before_rejections,
|
|
"rejected mode request corrupted persisted or host-visible state");
|
|
|
|
require(configuration_service_set_mode(
|
|
12, AdapterRequestedMode::kSwitch, implemented) ==
|
|
ConfigurationTransactionStatus::kPending,
|
|
"unchanged host mode was not serialized");
|
|
const int programs_before_unchanged = g_flash.program_count;
|
|
configuration_service_task_on_storage_core(2000);
|
|
require_mode_status(12, ConfigurationTransactionStatus::kUnchanged,
|
|
"unchanged host mode did not terminate unchanged");
|
|
require(g_flash.program_count == programs_before_unchanged,
|
|
"unchanged mode consumed a flash program");
|
|
require(configuration_service_mode_transaction_reboot_ready(12),
|
|
"current unchanged host mode could not authorize reboot");
|
|
|
|
constexpr uint32_t kInternalXInput = 0x80000020u;
|
|
require(configuration_service_set_mode_internal(
|
|
kInternalXInput, AdapterRequestedMode::kXInput,
|
|
implemented) == ConfigurationTransactionStatus::kPending &&
|
|
configuration_service_set_mode_internal(
|
|
kInternalXInput, AdapterRequestedMode::kXInput,
|
|
implemented) == ConfigurationTransactionStatus::kPending,
|
|
"accepted internal mode was not idempotently pending");
|
|
require(!configuration_service_mode_transaction_reboot_ready(12),
|
|
"accepted internal mode did not immediately supersede host reboot");
|
|
configuration_service_snapshot(&snapshot);
|
|
const ConfigurationTransactionSnapshot preserved_host =
|
|
snapshot.transaction;
|
|
require(configuration_service_begin(
|
|
20, ADAPTER_CONFIGURATION_SCHEMA_VERSION,
|
|
ADAPTER_CONFIGURATION_ENCODED_SIZE, 0) ==
|
|
ConfigurationTransactionStatus::kBusy &&
|
|
configuration_service_reset(21) ==
|
|
ConfigurationTransactionStatus::kBusy,
|
|
"host mutation was not blocked by pending internal mode");
|
|
configuration_service_task_on_storage_core(2000);
|
|
require_mode_status(12, ConfigurationTransactionStatus::kUnchanged,
|
|
"internal commit overwrote retained host status");
|
|
require_mode_status(kInternalXInput,
|
|
ConfigurationTransactionStatus::kCommitted,
|
|
"internal mode transaction did not commit");
|
|
require(!configuration_service_mode_transaction_reboot_ready(12) &&
|
|
configuration_service_mode_transaction_reboot_ready(
|
|
kInternalXInput),
|
|
"stale host generation authorized reboot after internal commit");
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.configuration.pairing_window_seconds == 90 &&
|
|
snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kXInput &&
|
|
snapshot.transaction.transaction_id ==
|
|
preserved_host.transaction_id &&
|
|
snapshot.transaction.status == preserved_host.status &&
|
|
snapshot.mode_transaction.transaction_id == kInternalXInput &&
|
|
snapshot.mode_transaction.stored_generation ==
|
|
snapshot.generation,
|
|
"internal mode changed host status or was not published");
|
|
require(configuration_service_set_mode(
|
|
13, AdapterRequestedMode::kAuto, implemented) ==
|
|
ConfigurationTransactionStatus::kPending,
|
|
"host mode did not serialize after internal mode");
|
|
require(!configuration_service_mode_transaction_reboot_ready(
|
|
kInternalXInput),
|
|
"accepted host mode did not immediately supersede internal reboot");
|
|
configuration_service_task_on_storage_core(3000);
|
|
require_mode_status(kInternalXInput,
|
|
ConfigurationTransactionStatus::kCommitted,
|
|
"host commit overwrote retained internal status");
|
|
require_mode_status(13, ConfigurationTransactionStatus::kCommitted,
|
|
"interleaved host mode transaction did not commit");
|
|
require(!configuration_service_mode_transaction_reboot_ready(
|
|
kInternalXInput) &&
|
|
configuration_service_mode_transaction_reboot_ready(13),
|
|
"stale internal generation authorized reboot after host commit");
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kAuto &&
|
|
snapshot.mode_transaction.transaction_id == 13 &&
|
|
!snapshot.mode_transaction.internal &&
|
|
snapshot.mode_transaction.stored_generation ==
|
|
snapshot.generation,
|
|
"latest host mode transaction was not published");
|
|
|
|
AdapterConfiguration generic = snapshot.configuration;
|
|
generic.pairing_window_seconds = 120;
|
|
uint8_t generic_payload[ADAPTER_CONFIGURATION_ENCODED_SIZE]{};
|
|
require(adapter_configuration_encode(generic, generic_payload,
|
|
sizeof(generic_payload)),
|
|
"generic test configuration did not encode");
|
|
const uint32_t generic_crc =
|
|
configuration_crc32(generic_payload, sizeof(generic_payload));
|
|
require(configuration_service_begin(
|
|
30, ADAPTER_CONFIGURATION_SCHEMA_VERSION,
|
|
sizeof(generic_payload), generic_crc) ==
|
|
ConfigurationTransactionStatus::kReceiving,
|
|
"generic host transaction did not begin");
|
|
require(!configuration_service_mode_transaction_reboot_ready(13),
|
|
"accepted generic configuration did not invalidate mode reboot");
|
|
require(configuration_service_set_mode_internal(
|
|
0x80000021u, AdapterRequestedMode::kAuto, implemented) ==
|
|
ConfigurationTransactionStatus::kBusy,
|
|
"internal mode displaced receiving host configuration");
|
|
require(configuration_service_append(
|
|
30, 0, generic_payload, sizeof(generic_payload)) ==
|
|
ConfigurationTransactionStatus::kReceiving &&
|
|
configuration_service_commit(30) ==
|
|
ConfigurationTransactionStatus::kPending,
|
|
"generic host configuration did not reach pending");
|
|
configuration_service_task_on_storage_core(4000);
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.configuration.pairing_window_seconds == 120 &&
|
|
snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kAuto &&
|
|
snapshot.transaction.transaction_id == 30 &&
|
|
snapshot.transaction.status ==
|
|
ConfigurationTransactionStatus::kCommitted,
|
|
"generic host commit did not preserve requested mode");
|
|
require_mode_status(13, ConfigurationTransactionStatus::kCommitted,
|
|
"generic commit erased retained host mode status");
|
|
require(!configuration_service_mode_transaction_reboot_ready(13),
|
|
"host mode authorized reboot for a stale stored generation");
|
|
ConfigurationTransactionStatus ignored{};
|
|
require(!configuration_service_mode_transaction_status(30, &ignored),
|
|
"generic transaction was misidentified as a mode transaction");
|
|
|
|
constexpr uint32_t kFailingInternal = 0x80000022u;
|
|
require(configuration_service_set_mode_internal(
|
|
kFailingInternal, AdapterRequestedMode::kXInput,
|
|
implemented) == ConfigurationTransactionStatus::kPending,
|
|
"failing internal mode was not queued");
|
|
g_flash.fail_program = true;
|
|
configuration_service_task_on_storage_core(5000);
|
|
g_flash.fail_program = false;
|
|
require_mode_status(kFailingInternal,
|
|
ConfigurationTransactionStatus::kStorageError,
|
|
"failed internal mode did not report storage error");
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.state == ConfigurationServiceState::kReady &&
|
|
snapshot.configuration.pairing_window_seconds == 120 &&
|
|
snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kAuto &&
|
|
snapshot.transaction.transaction_id == 30 &&
|
|
snapshot.transaction.status ==
|
|
ConfigurationTransactionStatus::kCommitted &&
|
|
!configuration_service_mode_transaction_reboot_ready(
|
|
kFailingInternal),
|
|
"failed internal mode did not roll back coherently");
|
|
|
|
const uint32_t reset_generation_before =
|
|
configuration_service_reset_generation();
|
|
require(configuration_service_reset(40) ==
|
|
ConfigurationTransactionStatus::kPending &&
|
|
configuration_service_reset_generation() ==
|
|
reset_generation_before + 1,
|
|
"configuration reset acceptance semantics changed");
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.configuration.pairing_window_seconds == 120 &&
|
|
snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kAuto,
|
|
"reset published defaults before durable commit");
|
|
configuration_service_task_on_storage_core(6000);
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.configuration.pairing_window_seconds ==
|
|
ADAPTER_PAIRING_WINDOW_SECONDS_DEFAULT &&
|
|
snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kAuto &&
|
|
snapshot.transaction.transaction_id == 40 &&
|
|
snapshot.transaction.status ==
|
|
ConfigurationTransactionStatus::kCommitted &&
|
|
snapshot.reset_generation == reset_generation_before + 1,
|
|
"configuration reset did not durably restore v2 defaults");
|
|
|
|
constexpr uint32_t kCapturedHostMode = 50;
|
|
require(configuration_service_set_mode(
|
|
kCapturedHostMode, AdapterRequestedMode::kSwitch,
|
|
implemented) == ConfigurationTransactionStatus::kPending,
|
|
"host mode was not queued for the storage completion race");
|
|
g_reserve_recovery_during_program = true;
|
|
configuration_service_task_on_storage_core(7000);
|
|
require_mode_status(
|
|
kCapturedHostMode, ConfigurationTransactionStatus::kBusy,
|
|
"in-flight host mode completion revived its canceled status");
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.transaction.transaction_id == kCapturedHostMode &&
|
|
snapshot.transaction.status ==
|
|
ConfigurationTransactionStatus::kBusy &&
|
|
snapshot.configuration.requested_mode ==
|
|
AdapterRequestedMode::kSwitch &&
|
|
!configuration_service_mode_transaction_reboot_ready(
|
|
kCapturedHostMode),
|
|
"captured host completion escaped recovery ownership");
|
|
|
|
const uint8_t blocked_payload = 0;
|
|
require(configuration_service_append(
|
|
kCapturedHostMode, 0, &blocked_payload,
|
|
sizeof(blocked_payload)) ==
|
|
ConfigurationTransactionStatus::kBusy &&
|
|
configuration_service_commit(kCapturedHostMode) ==
|
|
ConfigurationTransactionStatus::kBusy &&
|
|
configuration_service_begin(
|
|
51, ADAPTER_CONFIGURATION_SCHEMA_VERSION,
|
|
ADAPTER_CONFIGURATION_ENCODED_SIZE, 0) ==
|
|
ConfigurationTransactionStatus::kBusy &&
|
|
configuration_service_reset(52) ==
|
|
ConfigurationTransactionStatus::kBusy &&
|
|
configuration_service_set_mode(
|
|
53, AdapterRequestedMode::kSwitch, implemented) ==
|
|
ConfigurationTransactionStatus::kBusy,
|
|
"host mutation escaped recovery reservation");
|
|
require(configuration_service_set_mode_internal(
|
|
0x80000030u, AdapterRequestedMode::kSwitch,
|
|
implemented) == ConfigurationTransactionStatus::kBusy,
|
|
"non-Auto internal mode escaped recovery reservation");
|
|
|
|
constexpr uint32_t kRecoveryAuto = 0x80000031u;
|
|
require(configuration_service_set_mode_internal(
|
|
kRecoveryAuto, AdapterRequestedMode::kAuto,
|
|
implemented) == ConfigurationTransactionStatus::kPending &&
|
|
!configuration_service_mode_transaction_reboot_ready(
|
|
kRecoveryAuto),
|
|
"recovery Auto was blocked or authorized before persistence");
|
|
configuration_service_task_on_storage_core(8000);
|
|
require_mode_status(kRecoveryAuto,
|
|
ConfigurationTransactionStatus::kCommitted,
|
|
"recovery Auto did not overwrite canceled host mode");
|
|
require(configuration_service_mode_transaction_reboot_ready(
|
|
kRecoveryAuto),
|
|
"latest recovery Auto did not authorize reboot");
|
|
|
|
constexpr uint32_t kLatestRecoveryAuto = 0x80000032u;
|
|
require(configuration_service_set_mode_internal(
|
|
kLatestRecoveryAuto, AdapterRequestedMode::kAuto,
|
|
implemented) == ConfigurationTransactionStatus::kPending &&
|
|
!configuration_service_mode_transaction_reboot_ready(
|
|
kRecoveryAuto),
|
|
"newer recovery Auto did not immediately supersede reboot");
|
|
configuration_service_task_on_storage_core(9000);
|
|
require(!configuration_service_mode_transaction_reboot_ready(
|
|
kRecoveryAuto) &&
|
|
configuration_service_mode_transaction_reboot_ready(
|
|
kLatestRecoveryAuto),
|
|
"recovery reboot authorization did not remain latest-only");
|
|
}
|
|
|
|
void test_abandoned_host_receive_does_not_block_recovery() {
|
|
configuration_service_prepare();
|
|
configuration_service_initialize_pre_usb();
|
|
configuration_service_initialize_on_storage_core();
|
|
|
|
ConfigurationServiceSnapshot snapshot{};
|
|
configuration_service_snapshot(&snapshot);
|
|
AdapterConfiguration abandoned = snapshot.configuration;
|
|
abandoned.pairing_window_seconds = 180;
|
|
uint8_t payload[ADAPTER_CONFIGURATION_ENCODED_SIZE]{};
|
|
require(adapter_configuration_encode(abandoned, payload,
|
|
sizeof(payload)),
|
|
"abandoned recovery-race configuration did not encode");
|
|
const uint32_t crc = configuration_crc32(payload, sizeof(payload));
|
|
require(configuration_service_begin(
|
|
60, ADAPTER_CONFIGURATION_SCHEMA_VERSION,
|
|
sizeof(payload), crc) ==
|
|
ConfigurationTransactionStatus::kReceiving,
|
|
"abandoned host configuration did not begin receiving");
|
|
|
|
configuration_service_reserve_for_recovery();
|
|
configuration_service_reserve_for_recovery();
|
|
configuration_service_snapshot(&snapshot);
|
|
require(snapshot.transaction.transaction_id == 60 &&
|
|
snapshot.transaction.status ==
|
|
ConfigurationTransactionStatus::kBusy,
|
|
"recovery reservation did not terminally cancel host receive");
|
|
|
|
const AdapterModeAvailability implemented{true, true, true, true};
|
|
constexpr uint32_t kRecoveryAuto = 0x80000040u;
|
|
require(configuration_service_set_mode_internal(
|
|
kRecoveryAuto, AdapterRequestedMode::kAuto,
|
|
implemented) == ConfigurationTransactionStatus::kPending,
|
|
"abandoned host receive blocked recovery Auto");
|
|
configuration_service_task_on_storage_core(0);
|
|
require_mode_status(kRecoveryAuto,
|
|
ConfigurationTransactionStatus::kCommitted,
|
|
"recovery Auto did not commit after abandoned receive");
|
|
require(configuration_service_mode_transaction_reboot_ready(
|
|
kRecoveryAuto),
|
|
"recovery Auto did not become latest reboot authority");
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main(int argc, char**) {
|
|
if (argc > 1) {
|
|
test_abandoned_host_receive_does_not_block_recovery();
|
|
} else {
|
|
test_service_lifecycle_and_mutations();
|
|
}
|
|
return 0;
|
|
}
|