#pragma once #include #include #include #include #include namespace ota { enum class Phase { AConfirmed, PreparingB, BPrepared, TrialPending, TrialRunning, BConfirmed, RecoveryPending, RecoveryRunning, RecoveryChecked, SeparateRecoveryRequired }; enum class Slot { A, B }; const char* name(Phase phase); const char* name(Slot slot); struct State { Phase phase = Phase::AConfirmed; Slot normal = Slot::A; std::optional trial; int attempts_left = 0; bool a_ready = true; bool b_ready = false; std::string outcome; }; class Device { public: explicit Device(const std::filesystem::path& directory); const State& state() const { return state_; } std::optional running() const { return running_; } void prepare(bool complete = true); void request_trial(int attempts = 2); Slot boot(); void check(bool healthy); private: void save() const; State state_; std::filesystem::path path_; // Volatile: reconstructing a Device requires another boot before checks. std::optional running_; }; // Own only a freshly created directory and clean it up at scope exit. class TemporaryDirectory { public: TemporaryDirectory(); ~TemporaryDirectory(); TemporaryDirectory(const TemporaryDirectory&) = delete; TemporaryDirectory& operator=(const TemporaryDirectory&) = delete; const std::filesystem::path& path() const { return path_; } private: std::filesystem::path path_; }; const std::vector& scenarios(); Device run_scenario(const std::string& scenario, const std::filesystem::path& directory, std::ostream* output = nullptr); } // namespace ota