#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