From 02301271f49ffc9af7bc6423ac079df8778294d6 Mon Sep 17 00:00:00 2001 From: Kawin Pechetratanapanit <39807451+kawinie@users.noreply.github.com> Date: Fri, 17 Jul 2026 11:41:31 -0700 Subject: [PATCH 1/2] feat: restore last profile after boot Restore the last profile including voltage and current if index and V/A range are matched. If a charger swap changes that index, fall back to the picker instead of restoring the wrong output. This option replaces skip picker option. --- README.md | 4 +- include/v2/events.h | 23 +++- include/v2/hal/eeprom.h | 32 +++-- include/v2/preferences_store.h | 2 +- include/v2/stages/normal_stage.h | 41 ++++-- include/v2/stages/obtain_stage.h | 62 ++++++++- include/v2/stages/settings_stage.h | 30 ++--- include/v2/tasks/button_task.h | 2 +- include/v2/tasks/encoder_task.h | 2 +- include/v2/tasks/pps_control_task.h | 4 +- include/v2/tasks/preference_task.h | 77 +++++++++++ src/main.cpp | 9 +- test/test_v2_eeprom/test.cpp | 68 +++++++--- test/test_v2_inputs/test.cpp | 16 +-- test/test_v2_normal/test.cpp | 194 +++++++++++++++++++++++++--- test/test_v2_obtain/test.cpp | 46 ++++++- test/test_v2_pps_control/test.cpp | 34 ++--- test/test_v2_preference/test.cpp | 150 +++++++++++++++++++++ test/test_v2_resolve/test.cpp | 137 ++++++++++++++++++++ test/test_v2_sensor/test.cpp | 4 +- test/test_v2_settings/test.cpp | 42 +++--- 21 files changed, 837 insertions(+), 142 deletions(-) create mode 100644 include/v2/tasks/preference_task.h create mode 100644 test/test_v2_preference/test.cpp create mode 100644 test/test_v2_resolve/test.cpp diff --git a/README.md b/README.md index 02f0fb1..855ec5f 100644 --- a/README.md +++ b/README.md @@ -131,8 +131,8 @@ Hold the R button to open the energy screen. It shows power, live voltage and cu Hold the left button to open the menu. -- **Skip picker:** When true, PocketPD boots straight to the operating screen using the first profile 5V as default instead of stopping at the picker. -- **Voltage comp:** When true, PocketPD watches the load-side voltage and raises the PPS request in 20 mV steps, up to 500 mV, to cancel the drop across the cable and connectors. It runs only while the output is on and a PPS profile is active, and it resets whenever the output is off or you change profiles. +- **Restore profile:** When enabled, PocketPD saves the last profile plus its voltage/current and, on boot, restores them and jumps straight to the operating screen. When switching to another charger, PocketPD defaults to profile picker if the voltage/current fall outside of last profile range. +- **Voltage comp:** When enabled, PocketPD watches the load-side voltage and raises the PPS request in 20 mV steps, up to 500 mV, to cancel the drop across the cable and connectors. It runs only while the output is on and a PPS profile is active, and it resets whenever the output is off or you change profiles. ### Non-PD sources diff --git a/include/v2/events.h b/include/v2/events.h index 8f4b1ed..1239504 100644 --- a/include/v2/events.h +++ b/include/v2/events.h @@ -77,7 +77,7 @@ namespace pocketpd { uint32_t vbus_mv = 0; uint32_t current_ma = 0; - LoadReading ema(const LoadReading& sample ) const { + LoadReading ema(const LoadReading& sample) const { return { .timestamp_ms = sample.timestamp_ms, .vbus_mv = Filter::ema(vbus_mv, sample.vbus_mv, LOAD_EMA_DEN, SNAP_MV), @@ -137,8 +137,25 @@ namespace pocketpd { int32_t offset_mv = 0; }; + /** + * @brief Current output profile after any change. `pdo_index = -1` means no + * profile (passthrough). PreferenceTask records it as the last-used profile. + */ + struct ActiveProfileEvent { + bool is_pps = false; + int pdo_index = -1; + int mv = 0; + int ma = 0; + }; + using Event = tempo::Events< - PdReadyEvent, ButtonEvent, EncoderEvent, SensorEvent, EnergyEvent, - PpsTargetEvent, CompStateEvent>; + PdReadyEvent, + ButtonEvent, + EncoderEvent, + SensorEvent, + EnergyEvent, + PpsTargetEvent, + CompStateEvent, + ActiveProfileEvent>; } // namespace pocketpd diff --git a/include/v2/hal/eeprom.h b/include/v2/hal/eeprom.h index cb7f8d7..49a164a 100644 --- a/include/v2/hal/eeprom.h +++ b/include/v2/hal/eeprom.h @@ -18,23 +18,40 @@ namespace pocketpd { + struct LastProfile { + bool is_pps = false; + uint16_t voltage_mv = 0; + uint16_t current_ma = 0; + uint8_t pdo_index = 0; + }; + + inline bool operator==(const LastProfile& a, const LastProfile& b) { + return a.is_pps == b.is_pps && a.voltage_mv == b.voltage_mv && + a.current_ma == b.current_ma && a.pdo_index == b.pdo_index; + } + + inline bool operator!=(const LastProfile& a, const LastProfile& b) { + return !(a == b); + } + struct Preferences { - bool skip_picker_on_boot = false; - bool voltage_comp_enabled = false; - bool flip_display = false; + bool restore_last_profile_enabled = false; + bool voltage_compensate_enabled = false; + bool flip_display_enabled = false; + LastProfile last_profile; }; inline bool operator==(const Preferences& a, const Preferences& b) { - return a.skip_picker_on_boot == b.skip_picker_on_boot && - a.voltage_comp_enabled == b.voltage_comp_enabled && - a.flip_display == b.flip_display; + return a.restore_last_profile_enabled == b.restore_last_profile_enabled && + a.voltage_compensate_enabled == b.voltage_compensate_enabled && + a.flip_display_enabled == b.flip_display_enabled && a.last_profile == b.last_profile; } inline bool operator!=(const Preferences& a, const Preferences& b) { return !(a == b); } - static constexpr uint8_t PREFERENCES_LAYOUT_VERSION = 3; + static constexpr uint8_t PREFERENCES_LAYOUT_VERSION = 4; static constexpr size_t SIZE = sizeof(Preferences); static constexpr size_t EEPROM_PREFERENCES_BYTES = 1 + SIZE + 1; @@ -50,7 +67,6 @@ namespace pocketpd { return crc; } - inline void encode_preferences(const Preferences& payload, uint8_t* out) { out[0] = PREFERENCES_LAYOUT_VERSION; std::memcpy(out + 1, &payload, SIZE); diff --git a/include/v2/preferences_store.h b/include/v2/preferences_store.h index 591a16d..94844d7 100644 --- a/include/v2/preferences_store.h +++ b/include/v2/preferences_store.h @@ -35,7 +35,7 @@ namespace pocketpd { if (!m_eeprom.save(m_preferences)) { return false; } - + m_dirty = false; return true; } diff --git a/include/v2/stages/normal_stage.h b/include/v2/stages/normal_stage.h index 4a0c3e9..485e95f 100644 --- a/include/v2/stages/normal_stage.h +++ b/include/v2/stages/normal_stage.h @@ -38,19 +38,21 @@ namespace pocketpd { int8_t m_active_pdo_index = -1; int8_t m_last_active_index = -1; + int32_t m_restore_mv = -1; + int32_t m_restore_ma = -1; + Mode m_mode; - + IntervalTimer m_render_interval{40}; uint8_t m_arrow_frame = 0; uint32_t m_last_draw_ms = 0; bool m_blink_visible = true; - + bool m_locked = false; int32_t m_comp_offset_mv = 0; - - // After OFF->ON, the first INA226 read returns a stale conversion (latched while FET - // was off; observed ~200 mV instead of true VBUS). Discard N load samples and hold the - // seeded supply value until the sensor has a fresh conversion in hand. + + // After OFF->ON, the first INA226 read returns a stale conversion so we discard N load + // samples and hold the seeded supply value until the sensor is fresh. uint8_t m_postenable_discard_left = 0; static constexpr uint8_t POSTENABLE_DISCARD_SAMPLES = 2; @@ -110,8 +112,10 @@ namespace pocketpd { return p ? p->current_idx : 0; } - void prepare(int8_t pdo_index = -1) { + void prepare(int8_t pdo_index = -1, int32_t restore_mv = -1, int32_t restore_ma = -1) { m_active_pdo_index = pdo_index; + m_restore_mv = restore_mv; + m_restore_ma = restore_ma; } void on_enter(Conductor&, uint32_t) override { @@ -141,6 +145,8 @@ namespace pocketpd { enter_fixed_profile(); } + m_restore_mv = -1; + m_restore_ma = -1; m_last_active_index = m_active_pdo_index; draw(); } @@ -213,6 +219,14 @@ namespace pocketpd { pps->on_encoder(event); publish(PpsTargetEvent{m_active_pdo_index, pps->target_mv, pps->target_ma}); + publish( + ActiveProfileEvent{ + true, + m_active_pdo_index, + pps->target_mv, + pps->target_ma, + } + ); }, [&](const SensorEvent& event) { if (m_postenable_discard_left > 0) { @@ -233,9 +247,7 @@ namespace pocketpd { } } }, - [&](const CompStateEvent& evt) { - m_comp_offset_mv = evt.offset_mv; - }, + [&](const CompStateEvent& evt) { m_comp_offset_mv = evt.offset_mv; }, [](const auto&) {}, }; @@ -247,6 +259,10 @@ namespace pocketpd { bool same_profile = m_active_pdo_index == m_last_active_index; if (!same_profile) { PPSMode pps{m_pd_sink, m_active_pdo_index}; + if (m_restore_mv >= 0) { + pps.target_mv = m_restore_mv; + pps.target_ma = m_restore_ma; + } m_mode = pps; const auto msg = "Entered PPS profile pdo_index={} target_mv={} target_ma={}"; @@ -256,11 +272,13 @@ namespace pocketpd { auto& pps = std::get(m_mode); pps.clamp(); publish(PpsTargetEvent{m_active_pdo_index, pps.target_mv, pps.target_ma}); + publish(ActiveProfileEvent{true, m_active_pdo_index, pps.target_mv, pps.target_ma}); } void enter_fixed_profile() { + const int nominal_mv = m_pd_sink.pdo_max_voltage_mv(m_active_pdo_index); m_mode = FixedMode{ - .pdo_max_mv = m_pd_sink.pdo_max_voltage_mv(m_active_pdo_index), + .pdo_max_mv = nominal_mv, .pdo_max_ma = m_pd_sink.pdo_max_current_ma(m_active_pdo_index), }; @@ -269,6 +287,7 @@ namespace pocketpd { log.error("set_pdo({}) failed", m_active_pdo_index); } publish(PpsTargetEvent{-1, 0, 0}); + publish(ActiveProfileEvent{false, m_active_pdo_index, nominal_mv, 0}); } NormalViewModel build_view_model() const { diff --git a/include/v2/stages/obtain_stage.h b/include/v2/stages/obtain_stage.h index 1935c25..e24a4a2 100644 --- a/include/v2/stages/obtain_stage.h +++ b/include/v2/stages/obtain_stage.h @@ -4,23 +4,68 @@ */ #pragma once -#include -#include -#include - +#include #include #include #include +#include +#include +#include + #include "AP33772_debug.h" #include "v2/app.h" -#include "v2/preferences_store.h" #include "v2/events.h" #include "v2/hal/pd_sink_controller.h" #include "v2/pocketpd.h" +#include "v2/preferences_store.h" namespace pocketpd { + /** + * @brief Validate the saved profile against the PDO at its saved index and + * copy it into `out` ready to apply. PPS current is clamped to the PDO's + * max, and fixed current stays 0. + * + * @return false if restore is off, the index is gone, or the profile there + * no longer matches (out untouched). + */ + inline bool resolve_profile( + const Preferences& prefs, const PdSinkController& sink, LastProfile& out + ) { + if (!prefs.restore_last_profile_enabled) { + return false; + } + + const LastProfile& saved = prefs.last_profile; + const int index = static_cast(saved.pdo_index); + if (index < 0 || index >= sink.pdo_count()) { + return false; + } + + const int saved_mv = static_cast(saved.voltage_mv); + bool matches = false; + if (saved.is_pps) { + auto min_mv = sink.pdo_min_voltage_mv(index); + auto max_mv = sink.pdo_max_voltage_mv(index); + matches = sink.is_index_pps(index) && saved_mv >= min_mv && saved_mv <= max_mv; + } else { + matches = sink.is_index_fixed(index) && sink.pdo_max_voltage_mv(index) == saved_mv; + } + + if (!matches) { + return false; + } + + out = saved; + if (saved.is_pps) { + out.current_ma = static_cast( + std::min(static_cast(saved.current_ma), sink.pdo_max_current_ma(index)) + ); + } + return true; + } + class ObtainStage : public App::Stage, public App::UseLog, public App::UsePublisher { @@ -54,8 +99,11 @@ namespace pocketpd { log.error("PD negotiation failed"); } - if (m_prefs.get().skip_picker_on_boot) { - conductor.replace(); + LastProfile resolved; + if (resolve_profile(m_prefs.get(), m_pd_sink, resolved)) { + conductor.replace( + resolved.pdo_index, resolved.voltage_mv, resolved.current_ma + ); } } diff --git a/include/v2/stages/settings_stage.h b/include/v2/stages/settings_stage.h index e88531c..527e07f 100644 --- a/include/v2/stages/settings_stage.h +++ b/include/v2/stages/settings_stage.h @@ -26,8 +26,8 @@ namespace pocketpd { using Display = tempo::Display; enum class Item : uint8_t { - SKIP_PICKER, - VOLTAGE_COMP, + RESTORE_PROFILE, + VOLTAGE_COMPENSATE, FLIP_DISPLAY, }; @@ -37,8 +37,8 @@ namespace pocketpd { }; static constexpr std::array ITEMS = {{ - {Item::SKIP_PICKER, "Skip picker"}, - {Item::VOLTAGE_COMP, "Voltage comp"}, + {Item::RESTORE_PROFILE, "Restore profile"}, + {Item::VOLTAGE_COMPENSATE, "Voltage comp"}, {Item::FLIP_DISPLAY, "Flip display"}, }}; @@ -54,12 +54,12 @@ namespace pocketpd { bool value_at(Item item) const { const Preferences prefs = m_prefs.get(); switch (item) { - case Item::SKIP_PICKER: - return prefs.skip_picker_on_boot; - case Item::VOLTAGE_COMP: - return prefs.voltage_comp_enabled; + case Item::RESTORE_PROFILE: + return prefs.restore_last_profile_enabled; + case Item::VOLTAGE_COMPENSATE: + return prefs.voltage_compensate_enabled; case Item::FLIP_DISPLAY: - return prefs.flip_display; + return prefs.flip_display_enabled; } return false; } @@ -67,15 +67,15 @@ namespace pocketpd { void toggle_current() { Preferences prefs = m_prefs.get(); switch (ITEMS[m_table.cursor()].item) { - case Item::SKIP_PICKER: - prefs.skip_picker_on_boot = !prefs.skip_picker_on_boot; + case Item::RESTORE_PROFILE: + prefs.restore_last_profile_enabled = !prefs.restore_last_profile_enabled; break; - case Item::VOLTAGE_COMP: - prefs.voltage_comp_enabled = !prefs.voltage_comp_enabled; + case Item::VOLTAGE_COMPENSATE: + prefs.voltage_compensate_enabled = !prefs.voltage_compensate_enabled; break; case Item::FLIP_DISPLAY: - prefs.flip_display = !prefs.flip_display; - m_orientation.set_flipped(prefs.flip_display); + prefs.flip_display_enabled = !prefs.flip_display_enabled; + m_orientation.set_flipped(prefs.flip_display_enabled); break; } m_prefs.set(prefs); diff --git a/include/v2/tasks/button_task.h b/include/v2/tasks/button_task.h index 3c3922b..72e8b55 100644 --- a/include/v2/tasks/button_task.h +++ b/include/v2/tasks/button_task.h @@ -45,7 +45,7 @@ namespace pocketpd { * sits on the user's right. Swap L/R at publish time; ENCODER and L_R are symmetric. */ ButtonId published_id(ButtonId id) const { - if (!m_prefs.get().flip_display) { + if (!m_prefs.get().flip_display_enabled) { return id; } switch (id) { diff --git a/include/v2/tasks/encoder_task.h b/include/v2/tasks/encoder_task.h index 3a012dc..a02f1ad 100644 --- a/include/v2/tasks/encoder_task.h +++ b/include/v2/tasks/encoder_task.h @@ -44,7 +44,7 @@ namespace pocketpd { int delta = pos - m_last_position; if (delta != 0) { // Flipped display turns the knob around with the unit, so CW now reads as CCW. - if (m_prefs.get().flip_display) { + if (m_prefs.get().flip_display_enabled) { delta = -delta; } publish(EncoderEvent{delta}); diff --git a/include/v2/tasks/pps_control_task.h b/include/v2/tasks/pps_control_task.h index 041503c..49b997f 100644 --- a/include/v2/tasks/pps_control_task.h +++ b/include/v2/tasks/pps_control_task.h @@ -82,7 +82,7 @@ namespace pocketpd { } void on_tick(uint32_t) override { - if (!m_prefs.get().voltage_comp_enabled) { + if (!m_prefs.get().voltage_compensate_enabled) { clear_offset(); return; } @@ -145,7 +145,7 @@ namespace pocketpd { return false; } - const int offset = m_prefs.get().voltage_comp_enabled ? m_comp_offset_mv : 0; + const int offset = m_prefs.get().voltage_compensate_enabled ? m_comp_offset_mv : 0; const int request_mv = m_target_mv + offset; if (!m_sink.set_pps_pdo(m_pdo_index, request_mv, m_target_ma)) { log.error( diff --git a/include/v2/tasks/preference_task.h b/include/v2/tasks/preference_task.h new file mode 100644 index 0000000..7e8e3d1 --- /dev/null +++ b/include/v2/tasks/preference_task.h @@ -0,0 +1,77 @@ +/** + * @file preference_task.h + * @brief Owns runtime updates to PreferencesStore and debounced saves to flash. + */ +#pragma once + +#include +#include + +#include "v2/app.h" +#include "v2/events.h" +#include "v2/pocketpd.h" +#include "v2/preferences_store.h" + +namespace pocketpd { + + /** + * @brief Applies runtime preference updates via events and persists them. + * + */ + class PreferenceTask : public App::PeriodicTask, public App::UseLog { + private: + PreferencesStore& m_prefs; + Preferences m_last_seen{}; + uint32_t m_stamp_ms = 0; + + public: + static constexpr uint32_t PERIOD_MS = 250; + static constexpr const char* LOG_TAG = "Prefs"; + + explicit PreferenceTask(PreferencesStore& prefs) + : App::PeriodicTask(PERIOD_MS), m_prefs(prefs) {} + + const char* name() const override { + return LOG_TAG; + } + + void on_event(const Event& event, uint32_t) override { + const auto* profile = std::get_if(&event); + if (profile == nullptr || profile->pdo_index < 0) { + return; + } + + Preferences prefs = m_prefs.get(); + if (!prefs.restore_last_profile_enabled) { + return; + } + + prefs.last_profile = { + profile->is_pps, + static_cast(profile->mv), + static_cast(profile->ma), + static_cast(profile->pdo_index), + }; + + m_prefs.set(prefs); + } + + void on_tick(uint32_t now_ms) override { + const Preferences current = m_prefs.get(); + if (current != m_last_seen) { + m_last_seen = current; + m_stamp_ms = now_ms; + return; + } + + if (!m_prefs.dirty() || now_ms - m_stamp_ms < EEPROM_SAVE_DEBOUNCE_MS) { + return; + } + + if (!m_prefs.commit()) { + log.error("preference save failed"); + } + } + }; + +} // namespace pocketpd diff --git a/src/main.cpp b/src/main.cpp index 04f9167..06a1b93 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,6 @@ #include #include "v2/app.h" -#include "v2/preferences_store.h" #include "v2/hal/adc_supply_voltage_source.h" #include "v2/hal/ap33772_pd_sink.h" #include "v2/hal/ap33772_supply_voltage_source.h" @@ -19,12 +18,14 @@ #include "v2/hal/ina226_power_monitor.h" #include "v2/hal/rotary_encoder_input.h" #include "v2/hal/u8g2_display.h" +#include "v2/preferences_store.h" #include "v2/tasks/button_task.h" #include "v2/tasks/command_task.h" #include "v2/tasks/encoder_task.h" #include "v2/tasks/energy_task.h" -#include "v2/tasks/sensor_task.h" #include "v2/tasks/pps_control_task.h" +#include "v2/tasks/preference_task.h" +#include "v2/tasks/sensor_task.h" namespace pocketpd { @@ -78,6 +79,7 @@ namespace pocketpd { EnergyTask energy_task{output_gate}; CommandTask command_task{arduino_stream_reader, arduino_stream_writer}; PpsControlTask pps_control_task{prefs, output_gate, pd_sink}; + PreferenceTask preference_task{prefs}; } // namespace pocketpd @@ -100,7 +102,7 @@ void setup() { if (!prefs.load()) { Serial.println("[main] preferences load failed; defaults restored"); } - u8g2_display.set_flipped(prefs.get().flip_display); + u8g2_display.set_flipped(prefs.get().flip_display_enabled); encoder.begin(); app.register_stage(boot_stage); @@ -117,6 +119,7 @@ void setup() { app.add_task(energy_task); app.add_task(command_task); app.add_task(pps_control_task); + app.add_task(preference_task); app.start(); } diff --git a/test/test_v2_eeprom/test.cpp b/test/test_v2_eeprom/test.cpp index 33104a8..cec3214 100644 --- a/test/test_v2_eeprom/test.cpp +++ b/test/test_v2_eeprom/test.cpp @@ -1,10 +1,10 @@ #define VERSION "\"test\"" -#include - #include #include +#include + #include "v2/hal/eeprom.h" using namespace pocketpd; @@ -17,17 +17,17 @@ TEST(EepromCodec, RoundTripDefaultsThenCustom) { Preferences out{}; EXPECT_TRUE(decode_preferences(buf.data(), out)); - EXPECT_EQ(out.skip_picker_on_boot, false); + EXPECT_EQ(out.restore_last_profile_enabled, false); - Preferences in_custom{ .skip_picker_on_boot = true }; + Preferences in_custom{.restore_last_profile_enabled = true}; encode_preferences(in_custom, buf.data()); EXPECT_TRUE(decode_preferences(buf.data(), out)); - EXPECT_EQ(out.skip_picker_on_boot, true); + EXPECT_EQ(out.restore_last_profile_enabled, true); } TEST(EepromCodec, VersionMismatchFailsDecode) { std::array buf{}; - Preferences in{ .skip_picker_on_boot = true }; + Preferences in{.restore_last_profile_enabled = true}; encode_preferences(in, buf.data()); buf[0] = PREFERENCES_LAYOUT_VERSION + 1; @@ -38,7 +38,7 @@ TEST(EepromCodec, VersionMismatchFailsDecode) { TEST(EepromCodec, CrcCorruptionFailsDecode) { std::array buf{}; - Preferences in{ .skip_picker_on_boot = true }; + Preferences in{.restore_last_profile_enabled = true}; encode_preferences(in, buf.data()); buf[1] ^= 0xFF; @@ -51,40 +51,74 @@ TEST(EepromCodec, VoltageCompFieldRoundTrips) { std::array buf{}; Preferences in{ - .skip_picker_on_boot = false, - .voltage_comp_enabled = true, + .restore_last_profile_enabled = false, + .voltage_compensate_enabled = true, }; encode_preferences(in, buf.data()); Preferences out{}; EXPECT_TRUE(decode_preferences(buf.data(), out)); - EXPECT_FALSE(out.skip_picker_on_boot); - EXPECT_TRUE(out.voltage_comp_enabled); + EXPECT_FALSE(out.restore_last_profile_enabled); + EXPECT_TRUE(out.voltage_compensate_enabled); } TEST(EepromCodec, DefaultsHaveVoltageCompOff) { Preferences p{}; - EXPECT_FALSE(p.voltage_comp_enabled); + EXPECT_FALSE(p.voltage_compensate_enabled); } TEST(EepromCodec, FlipDisplayFieldRoundTrips) { std::array buf{}; Preferences in{ - .skip_picker_on_boot = false, - .voltage_comp_enabled = false, - .flip_display = true, + .restore_last_profile_enabled = false, + .voltage_compensate_enabled = false, + .flip_display_enabled = true, }; encode_preferences(in, buf.data()); Preferences out{}; EXPECT_TRUE(decode_preferences(buf.data(), out)); - EXPECT_TRUE(out.flip_display); + EXPECT_TRUE(out.flip_display_enabled); } TEST(EepromCodec, DefaultsHaveFlipDisplayOff) { Preferences p{}; - EXPECT_FALSE(p.flip_display); + EXPECT_FALSE(p.flip_display_enabled); +} + +TEST(EepromCodec, LayoutVersionIsFour) { + EXPECT_EQ(PREFERENCES_LAYOUT_VERSION, 4); +} + +TEST(EepromCodec, SavedProfileFieldsRoundTrip) { + std::array buf{}; + + Preferences in{ + .restore_last_profile_enabled = true, + .last_profile = { + .is_pps = true, + .voltage_mv = 9000, + .current_ma = 2500, + .pdo_index = 3, + }, + }; + encode_preferences(in, buf.data()); + + Preferences out{}; + EXPECT_TRUE(decode_preferences(buf.data(), out)); + EXPECT_TRUE(out.last_profile.is_pps); + EXPECT_EQ(out.last_profile.voltage_mv, 9000); + EXPECT_EQ(out.last_profile.current_ma, 2500); + EXPECT_EQ(out.last_profile.pdo_index, 3); +} + +TEST(EepromCodec, SavedProfileDefaultsAreZero) { + Preferences p{}; + EXPECT_FALSE(p.last_profile.is_pps); + EXPECT_EQ(p.last_profile.voltage_mv, 0); + EXPECT_EQ(p.last_profile.current_ma, 0); + EXPECT_EQ(p.last_profile.pdo_index, 0); } int main(int argc, char** argv) { diff --git a/test/test_v2_inputs/test.cpp b/test/test_v2_inputs/test.cpp index 221da16..ff61906 100644 --- a/test/test_v2_inputs/test.cpp +++ b/test/test_v2_inputs/test.cpp @@ -7,6 +7,8 @@ */ #define VERSION "\"test\"" +#include + #include #include #include @@ -15,8 +17,6 @@ #include #include -#include - #include "v2/events.h" #include "v2/input/button_gesture.h" #include "v2/input/two_buttons_gesture.h" @@ -187,7 +187,7 @@ TEST(ButtonTask, FlipDisplaySwapsPublishedLR) { ButtonTask task(encoder, l, r, prefs); task.attach_publisher_INTERNAL_DO_NOT_USE(pub); - prefs.set({.flip_display = true}); + prefs.set({.flip_display_enabled = true}); l.set_held(true); task.poll(0); @@ -217,7 +217,7 @@ TEST(ButtonTask, FlipDisplayLeavesEncoderAndComboAlone) { ButtonTask task(encoder, l, r, prefs); task.attach_publisher_INTERNAL_DO_NOT_USE(pub); - prefs.set({.flip_display = true}); + prefs.set({.flip_display_enabled = true}); encoder.set_held(true); task.poll(0); @@ -317,7 +317,7 @@ TEST(EncoderTask, FlipDisplayNegatesDelta) { EncoderTask task(enc, prefs); task.attach_publisher_INTERNAL_DO_NOT_USE(pub); - prefs.set({.flip_display = true}); + prefs.set({.flip_display_enabled = true}); task.on_start(); enc.set_position(3); @@ -515,7 +515,7 @@ TEST(TwoButtonsGestureDetector, AbortedPersistsWhileOneHeld) { TEST(TwoButtonsGestureDetector, AbortClearsOnlyAfterBothReleased) { TwoButtonsGestureDetector d; d.update(0, true, true); - d.update(100, true, false); // latched (aborted) + d.update(100, true, false); // latched (aborted) d.update(110, true, false); EXPECT_TRUE(d.is_active()); @@ -530,7 +530,7 @@ TEST(TwoButtonsGestureDetector, AbortClearsOnlyAfterBothReleased) { TEST(TwoButtonsGestureDetector, FiredPersistsWhileOneHeld) { TwoButtonsGestureDetector d; d.update(0, true, true); - d.update(kDefaultCfg.long_press_ms, true, true); // fired → LATCHED + d.update(kDefaultCfg.long_press_ms, true, true); // fired → LATCHED d.update(kDefaultCfg.long_press_ms + 50, true, false); EXPECT_TRUE(d.is_active()); } @@ -547,7 +547,7 @@ TEST(TwoButtonsGestureDetector, SecondPressArmsAtSecondPressTime) { // L pressed at t=0, R pressed at t=80; chord timer should start at t=80 // and fire at t=80 + long_press_ms. TwoButtonsGestureDetector d; - d.update(0, true, false); // only L held + d.update(0, true, false); // only L held d.update(80, true, true); EXPECT_TRUE(d.is_active()); diff --git a/test/test_v2_normal/test.cpp b/test/test_v2_normal/test.cpp index 41d916b..e13d7cd 100644 --- a/test/test_v2_normal/test.cpp +++ b/test/test_v2_normal/test.cpp @@ -42,7 +42,7 @@ namespace { } return std::get_if(&last); } -} +} // namespace TEST(NormalStage, OnEnterPdoProfileRequestsFixedPdo) { NiceMock display; @@ -71,7 +71,7 @@ TEST(NormalStage, OnEnterPpsProfileResetsTargetsToDefaults) { EXPECT_CALL(sink, pdo_max_voltage_mv(1)).WillRepeatedly(Return(11000)); EXPECT_CALL(sink, pdo_max_current_ma(1)).WillRepeatedly(Return(3000)); EXPECT_CALL(sink, set_pdo).Times(0); - EXPECT_CALL(sink, set_pps_pdo).Times(0); // PpsControlTask owns sink writes + EXPECT_CALL(sink, set_pps_pdo).Times(0); // PpsControlTask owns sink writes NormalStage normal(display, sink, gate); TestConductor conductor; @@ -149,8 +149,7 @@ TEST(NormalStage, OnEnterWithNegativeIndexRendersPassthrough) { EXPECT_CALL(sink, set_pps_pdo).Times(0); EXPECT_CALL(display, clear()).Times(::testing::AtLeast(1)); EXPECT_CALL(display, draw_text(_, _, _)).Times(AnyNumber()); - EXPECT_CALL(display, draw_text(_, _, HasSubstr("Passthrough"))) - .Times(::testing::AtLeast(1)); + EXPECT_CALL(display, draw_text(_, _, HasSubstr("Passthrough"))).Times(::testing::AtLeast(1)); EXPECT_CALL(display, flush()).Times(::testing::AtLeast(1)); NormalStage normal(display, sink, gate); @@ -375,9 +374,7 @@ TEST(NormalStage, OnEnterPdoBranchRendersVAReadoutAndPdoIndex) { conductor.register_stage(normal); normal.prepare(2); normal.on_event( - conductor, - SensorEvent{LoadReading{0, 5000, 1234}, SupplyReading{0, 20000, true}}, - 0 + conductor, SensorEvent{LoadReading{0, 5000, 1234}, SupplyReading{0, 20000, true}}, 0 ); conductor.start(0); } @@ -389,11 +386,14 @@ TEST(NormalView, LockedRendersPadlock) { EXPECT_CALL( display, draw_xbm( - NormalView::PADLOCK_X, NormalView::PADLOCK_Y, - NormalView::PADLOCK_W, NormalView::PADLOCK_H, + NormalView::PADLOCK_X, + NormalView::PADLOCK_Y, + NormalView::PADLOCK_W, + NormalView::PADLOCK_H, bitmap::PADLOCK.data() ) - ).Times(1); + ) + .Times(1); NormalViewModel vm{}; vm.mode = FixedMode{}; @@ -411,7 +411,8 @@ TEST(NormalView, UnlockedDoesNotDrawPadlock) { EXPECT_CALL( display, draw_xbm(_, _, NormalView::PADLOCK_W, NormalView::PADLOCK_H, bitmap::PADLOCK.data()) - ).Times(0); + ) + .Times(0); NormalViewModel vm{}; vm.mode = FixedMode{}; @@ -581,11 +582,15 @@ namespace { bool find_pps_event(TestQueue& q, Pred match) { while (true) { const PpsTargetEvent* evt = pop_as(q); - if (evt == nullptr) return false; // queue empty - if (match(*evt)) return true; + if (evt == nullptr) { + return false; // queue empty + } + if (match(*evt)) { + return true; + } } } -} +} // namespace TEST(NormalStagePublishing, EmitsPpsTargetOnPpsEntry) { NiceMock display; @@ -651,9 +656,7 @@ TEST(NormalStagePublishing, EmitsInactivePpsOnPassthroughEntry) { conductor.register_stage(normal); conductor.start(0); - EXPECT_TRUE(find_pps_event(queue, [](const PpsTargetEvent& e) { - return e.pdo_index == -1; - })); + EXPECT_TRUE(find_pps_event(queue, [](const PpsTargetEvent& e) { return e.pdo_index == -1; })); } TEST(NormalStagePublishing, EmitsRefreshedPpsTargetOnEncoderApply) { @@ -678,7 +681,8 @@ TEST(NormalStagePublishing, EmitsRefreshedPpsTargetOnEncoderApply) { conductor.start(0); // Drain entry events. - while (pop_as(queue) != nullptr) {} + while (pop_as(queue) != nullptr) { + } normal.on_event(conductor, EncoderEvent{-1}, 0); @@ -726,7 +730,7 @@ namespace { mode.target_ma = target_ma; return mode; } -} +} // namespace TEST(PpsViewRender, OffsetSuffixHiddenWhenZero) { NiceMock display; @@ -768,6 +772,158 @@ TEST(PpsViewRender, OffsetSuffixRenderedWhenNonZero) { NormalView::render(display, vm); } +TEST(NormalStage, PpsRestoreSeedsSavedTarget) { + NiceMock display; + NiceMock sink; + NiceMock gate; + EXPECT_CALL(sink, is_index_pps(1)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_min_voltage_mv(1)).WillRepeatedly(Return(3300)); + EXPECT_CALL(sink, pdo_max_voltage_mv(1)).WillRepeatedly(Return(11000)); + EXPECT_CALL(sink, pdo_max_current_ma(1)).WillRepeatedly(Return(3000)); + EXPECT_CALL(sink, set_pps_pdo).WillRepeatedly(Return(true)); + + NormalStage normal(display, sink, gate); + TestConductor conductor; + conductor.register_stage(normal); + normal.prepare(1, 9000, 2500); + conductor.start(0); + + EXPECT_EQ(normal.target_mv(), 9000); + EXPECT_EQ(normal.target_ma(), 2500); +} + +TEST(NormalStage, PpsRestoreClampsToPdoRange) { + NiceMock display; + NiceMock sink; + NiceMock gate; + EXPECT_CALL(sink, is_index_pps(1)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_min_voltage_mv(1)).WillRepeatedly(Return(3300)); + EXPECT_CALL(sink, pdo_max_voltage_mv(1)).WillRepeatedly(Return(11000)); + EXPECT_CALL(sink, pdo_max_current_ma(1)).WillRepeatedly(Return(3000)); + EXPECT_CALL(sink, set_pps_pdo).WillRepeatedly(Return(true)); + + NormalStage normal(display, sink, gate); + TestConductor conductor; + conductor.register_stage(normal); + normal.prepare(1, 15000, 5000); + conductor.start(0); + + EXPECT_EQ(normal.target_mv(), 11000); + EXPECT_EQ(normal.target_ma(), 3000); +} + +TEST(NormalStage, PrepareWithoutRestoreUsesDefaults) { + NiceMock display; + NiceMock sink; + NiceMock gate; + EXPECT_CALL(sink, is_index_pps(1)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_min_voltage_mv(1)).WillRepeatedly(Return(3300)); + EXPECT_CALL(sink, pdo_max_voltage_mv(1)).WillRepeatedly(Return(11000)); + EXPECT_CALL(sink, pdo_max_current_ma(1)).WillRepeatedly(Return(3000)); + EXPECT_CALL(sink, set_pps_pdo).WillRepeatedly(Return(true)); + + NormalStage normal(display, sink, gate); + TestConductor conductor; + conductor.register_stage(normal); + normal.prepare(1); + conductor.start(0); + + EXPECT_EQ(normal.target_mv(), 5000); + EXPECT_EQ(normal.target_ma(), 1000); +} + +namespace { + // NormalStage publishes PpsTargetEvent immediately before ActiveProfileEvent at every site, + // so the first item this scan pops is never the target. Loop on queue emptiness rather than + // pop_as's nullptr (which can't tell "empty" from "wrong type"). + template + bool find_active_event(TestQueue& q, Pred match) { + while (!q.empty()) { + const ActiveProfileEvent* evt = pop_as(q); + if (evt != nullptr && match(*evt)) { + return true; + } + } + return false; + } +} // namespace + +TEST(NormalStagePublishing, EmitsActiveProfileOnPpsEntry) { + NiceMock display; + NiceMock sink; + NiceMock gate; + EXPECT_CALL(sink, is_index_pps(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_min_voltage_mv(_)).WillRepeatedly(Return(3300)); + EXPECT_CALL(sink, pdo_max_voltage_mv(_)).WillRepeatedly(Return(11000)); + EXPECT_CALL(sink, pdo_max_current_ma(_)).WillRepeatedly(Return(3000)); + EXPECT_CALL(sink, set_pps_pdo).WillRepeatedly(Return(true)); + + NormalStage normal(display, sink, gate); + TestQueue queue; + TestPublisher publisher(queue); + normal.attach_publisher_INTERNAL_DO_NOT_USE(publisher); + + TestConductor conductor; + conductor.register_stage(normal); + normal.prepare(1); + conductor.start(0); + + EXPECT_TRUE(find_active_event(queue, [](const ActiveProfileEvent& e) { + return e.is_pps && e.pdo_index == 1 && e.mv == 5000 && e.ma == 1000; + })); +} + +TEST(NormalStagePublishing, EmitsActiveProfileOnFixedEntry) { + NiceMock display; + NiceMock sink; + NiceMock gate; + EXPECT_CALL(sink, is_index_pps(_)).WillRepeatedly(Return(false)); + EXPECT_CALL(sink, pdo_max_voltage_mv(2)).WillRepeatedly(Return(20000)); + EXPECT_CALL(sink, pdo_max_current_ma(2)).WillRepeatedly(Return(5000)); + EXPECT_CALL(sink, set_pdo(2)).WillRepeatedly(Return(true)); + + NormalStage normal(display, sink, gate); + TestQueue queue; + TestPublisher publisher(queue); + normal.attach_publisher_INTERNAL_DO_NOT_USE(publisher); + + TestConductor conductor; + conductor.register_stage(normal); + normal.prepare(2); + conductor.start(0); + + EXPECT_TRUE(find_active_event(queue, [](const ActiveProfileEvent& e) { + return !e.is_pps && e.pdo_index == 2 && e.mv == 20000 && e.ma == 0; + })); +} + +TEST(NormalStagePublishing, EmitsActiveProfileOnEncoderEdit) { + NiceMock display; + NiceMock sink; + NiceMock gate; + EXPECT_CALL(sink, is_index_pps(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_min_voltage_mv(_)).WillRepeatedly(Return(3300)); + EXPECT_CALL(sink, pdo_max_voltage_mv(_)).WillRepeatedly(Return(11000)); + EXPECT_CALL(sink, pdo_max_current_ma(_)).WillRepeatedly(Return(3000)); + EXPECT_CALL(sink, set_pps_pdo).WillRepeatedly(Return(true)); + + NormalStage normal(display, sink, gate); + TestQueue queue; + TestPublisher publisher(queue); + normal.attach_publisher_INTERNAL_DO_NOT_USE(publisher); + + TestConductor conductor; + conductor.register_stage(normal); + normal.prepare(0); + conductor.start(0); + + normal.on_event(conductor, EncoderEvent{-1}, 0); + const int expected = 5000 + static_cast(VOLTAGE_INCREMENTS_MV[0]); + EXPECT_TRUE(find_active_event(queue, [&](const ActiveProfileEvent& e) { + return e.is_pps && e.pdo_index == 0 && e.mv == expected; + })); +} + int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); diff --git a/test/test_v2_obtain/test.cpp b/test/test_v2_obtain/test.cpp index 300b33c..4f9f3be 100644 --- a/test/test_v2_obtain/test.cpp +++ b/test/test_v2_obtain/test.cpp @@ -12,9 +12,9 @@ #include #include "v2/app.h" -#include "v2/preferences_store.h" #include "v2/events.h" #include "v2/hal/eeprom.h" +#include "v2/preferences_store.h" #include "v2/stages/boot_stage.h" #include "v2/stages/normal_stage.h" #include "v2/stages/obtain_stage.h" @@ -153,15 +153,23 @@ TEST(BootStage, RequestsObtainAfterTimeout) { EXPECT_EQ(conductor.current_index(), TestConductor::index_of()); } -TEST(ObtainStage, SkipPickerOnBootRequestsNormal) { +TEST(ObtainStage, RememberedProfileResolvesToNormal) { NiceMock sink; NiceMock mock_eeprom; - PreferencesStore config{mock_eeprom, Preferences{.skip_picker_on_boot = true}}; + PreferencesStore config{ + mock_eeprom, + Preferences{ + .restore_last_profile_enabled = true, + .last_profile = {.is_pps = false, .voltage_mv = 9000, .pdo_index = 1}, + } + }; NiceMock display; NiceMock gate; EXPECT_CALL(sink, begin()).WillOnce(Return(true)); EXPECT_CALL(sink, pdo_count()).WillRepeatedly(Return(2)); + EXPECT_CALL(sink, is_index_fixed(::testing::_)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_max_voltage_mv(::testing::_)).WillRepeatedly(Return(9000)); EXPECT_CALL(sink, is_index_pps(::testing::_)).WillRepeatedly(Return(false)); EXPECT_CALL(sink, set_pdo).WillRepeatedly(Return(true)); @@ -177,6 +185,38 @@ TEST(ObtainStage, SkipPickerOnBootRequestsNormal) { EXPECT_EQ(conductor.current_index(), TestConductor::index_of()); } +TEST(ObtainStage, RememberOnButNoMatchFallsToPicker) { + NiceMock sink; + NiceMock mock_eeprom; + PreferencesStore config{ + mock_eeprom, + Preferences{ + .restore_last_profile_enabled = true, + .last_profile = {.is_pps = false, .voltage_mv = 9000}, + } + }; + NiceMock display; + + EXPECT_CALL(sink, begin()).WillOnce(Return(true)); + EXPECT_CALL(sink, pdo_count()).WillRepeatedly(Return(2)); + EXPECT_CALL(sink, is_index_fixed(::testing::_)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_max_voltage_mv(::testing::_)).WillRepeatedly(Return(20000)); + + ObtainStage stage(sink, config); + ProfilePickerStage picker(display, sink); + TestConductor conductor; + conductor.register_stage(stage); + conductor.register_stage(picker); + conductor.start(0); + + EXPECT_FALSE(conductor.has_pending()); + + conductor.tick(OBTAIN_TO_PROFILE_PICKER_MS); + EXPECT_TRUE(conductor.has_pending()); + EXPECT_TRUE(conductor.apply_pending_transition(0)); + EXPECT_EQ(conductor.current_index(), TestConductor::index_of()); +} + TEST(ObtainStage, SkipPickerDisabledFollowsNormalTimeoutPath) { NiceMock sink; NiceMock mock_eeprom; diff --git a/test/test_v2_pps_control/test.cpp b/test/test_v2_pps_control/test.cpp index 1b1e077..5cf2f2a 100644 --- a/test/test_v2_pps_control/test.cpp +++ b/test/test_v2_pps_control/test.cpp @@ -12,9 +12,9 @@ #include "v2/tasks/pps_control_task.h" using namespace pocketpd; +using ::testing::_; using ::testing::NiceMock; using ::testing::Return; -using ::testing::_; namespace { @@ -67,7 +67,7 @@ TEST(PpsControlTask, WritesBareTargetOnPpsTargetWhenDisabled) { TEST(PpsControlTask, WritesBareTargetOnPpsTargetWhenEnabledButOffsetIsZero) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.sink, set_pps_pdo(0, 5000, 1000)).Times(1).WillOnce(Return(true)); h.task.on_event(PpsTargetEvent{0, 5000, 1000}, 0); @@ -75,7 +75,7 @@ TEST(PpsControlTask, WritesBareTargetOnPpsTargetWhenEnabledButOffsetIsZero) { TEST(PpsControlTask, ReissuesWithOffsetOnSamePdoTargetChange) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()).WillRepeatedly(Return(true)); // Build offset = 60 via ticks. @@ -96,7 +96,7 @@ TEST(PpsControlTask, ReissuesWithOffsetOnSamePdoTargetChange) { TEST(PpsControlTask, NewPdoResetsOffsetAndWritesBareTarget) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()).WillRepeatedly(Return(true)); EXPECT_CALL(h.sink, set_pps_pdo).WillRepeatedly(Return(true)); @@ -116,7 +116,7 @@ TEST(PpsControlTask, NewPdoResetsOffsetAndWritesBareTarget) { TEST(PpsControlTaskTick, StepsUpOncePerTickWhenErrorPositive) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()).WillRepeatedly(Return(true)); ::testing::InSequence seq; @@ -131,7 +131,7 @@ TEST(PpsControlTaskTick, StepsUpOncePerTickWhenErrorPositive) { TEST(PpsControlTaskTick, DeadBandSuppressesUpdates) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()).WillRepeatedly(Return(true)); EXPECT_CALL(h.sink, set_pps_pdo(0, 5000, 1000)).Times(1).WillOnce(Return(true)); @@ -143,7 +143,7 @@ TEST(PpsControlTaskTick, DeadBandSuppressesUpdates) { TEST(PpsControlTaskTick, NegativeErrorStepsDown) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()).WillRepeatedly(Return(true)); EXPECT_CALL(h.sink, set_pps_pdo).WillRepeatedly(Return(true)); @@ -163,7 +163,7 @@ TEST(PpsControlTaskTick, NegativeErrorStepsDown) { TEST(PpsControlTaskTick, OutputOffResetsOffset) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()) .WillOnce(Return(true)) @@ -192,7 +192,7 @@ TEST(PpsControlTaskTick, OutputOffResetsOffset) { TEST(PpsControlTaskTick, SaturatesAtMaxComp) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()).WillRepeatedly(Return(true)); EXPECT_CALL(h.sink, set_pps_pdo).WillRepeatedly(Return(true)); @@ -212,14 +212,14 @@ TEST(PpsControlTaskTick, SaturatesAtMaxComp) { TEST(PpsControlTaskTick, FailedSinkOnTickHoldsOffsetForRetry) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()).WillRepeatedly(Return(true)); { ::testing::InSequence seq; - EXPECT_CALL(h.sink, set_pps_pdo(0, 5000, 1000)).WillOnce(Return(true)); // initial OK - EXPECT_CALL(h.sink, set_pps_pdo(0, 5020, 1000)).WillOnce(Return(false)); // tick fails - EXPECT_CALL(h.sink, set_pps_pdo(0, 5020, 1000)).WillOnce(Return(true)); // retry OK + EXPECT_CALL(h.sink, set_pps_pdo(0, 5000, 1000)).WillOnce(Return(true)); // initial OK + EXPECT_CALL(h.sink, set_pps_pdo(0, 5020, 1000)).WillOnce(Return(false)); // tick fails + EXPECT_CALL(h.sink, set_pps_pdo(0, 5020, 1000)).WillOnce(Return(true)); // retry OK } h.task.on_event(PpsTargetEvent{0, 5000, 1000}, 0); @@ -233,7 +233,7 @@ TEST(PpsControlTaskTick, FailedSinkOnTickHoldsOffsetForRetry) { TEST(PpsControlTaskTick, PreferenceFlipOffEmitsCleanupAndZeroes) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()).WillRepeatedly(Return(true)); EXPECT_CALL(h.sink, set_pps_pdo).Times(3).WillRepeatedly(Return(true)); @@ -243,7 +243,7 @@ TEST(PpsControlTaskTick, PreferenceFlipOffEmitsCleanupAndZeroes) { h.task.on_tick(2 * PpsControlTask::PERIOD_MS); EXPECT_EQ(h.task.comp_offset_mv(), 40); - h.prefs.set({.voltage_comp_enabled = false}); + h.prefs.set({.voltage_compensate_enabled = false}); ::testing::Mock::VerifyAndClearExpectations(&h.sink); EXPECT_CALL(h.sink, set_pps_pdo(0, 5000, 1000)).Times(1).WillOnce(Return(true)); @@ -257,7 +257,7 @@ TEST(PpsControlTaskTick, PreferenceFlipOffEmitsCleanupAndZeroes) { TEST(PpsControlTaskTick, ClampsRequestToPdoMax) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()).WillRepeatedly(Return(true)); EXPECT_CALL(h.sink, pdo_max_voltage_mv(0)).WillRepeatedly(Return(5000)); @@ -272,7 +272,7 @@ TEST(PpsControlTaskTick, ClampsRequestToPdoMax) { TEST(PpsControlTaskTick, PartialPdoClampStopsAtCeiling) { Harness h; - h.prefs.set({.voltage_comp_enabled = true}); + h.prefs.set({.voltage_compensate_enabled = true}); EXPECT_CALL(h.gate, is_enabled()).WillRepeatedly(Return(true)); EXPECT_CALL(h.sink, pdo_max_voltage_mv(0)).WillRepeatedly(Return(5010)); diff --git a/test/test_v2_preference/test.cpp b/test/test_v2_preference/test.cpp new file mode 100644 index 0000000..d296951 --- /dev/null +++ b/test/test_v2_preference/test.cpp @@ -0,0 +1,150 @@ +#define VERSION "\"test\"" + +#include +#include +#include + +#include "v2/app.h" +#include "v2/events.h" +#include "v2/preferences_store.h" +#include "v2/tasks/preference_task.h" + +using namespace pocketpd; +using ::testing::_; +using ::testing::NiceMock; +using ::testing::Return; + +namespace { + Preferences remember_on() { + return Preferences{.restore_last_profile_enabled = true}; + } +} // namespace + +// —— Updating the store from events + +TEST(PreferenceTask, RecordsProfileFromEventWhenRememberOn) { + NiceMock eeprom; + PreferencesStore prefs{eeprom, remember_on()}; + PreferenceTask task{prefs}; + + task.on_event(ActiveProfileEvent{true, 1, 9000, 2000}, 0); + + const LastProfile lp = prefs.get().last_profile; + EXPECT_TRUE(lp.is_pps); + EXPECT_EQ(lp.pdo_index, 1); + EXPECT_EQ(lp.voltage_mv, 9000); + EXPECT_EQ(lp.current_ma, 2000); + EXPECT_TRUE(prefs.dirty()); +} + +TEST(PreferenceTask, IgnoresProfileEventWhenRememberOff) { + NiceMock eeprom; + PreferencesStore prefs{eeprom}; // remember defaults off + PreferenceTask task{prefs}; + + task.on_event(ActiveProfileEvent{true, 1, 9000, 2000}, 0); + + EXPECT_EQ(prefs.get().last_profile.voltage_mv, 0); + EXPECT_FALSE(prefs.dirty()); +} + +TEST(PreferenceTask, IgnoresPassthroughProfileEvent) { + NiceMock eeprom; + PreferencesStore prefs{eeprom, remember_on()}; + PreferenceTask task{prefs}; + + task.on_event(ActiveProfileEvent{false, -1, 0, 0}, 0); + + EXPECT_FALSE(prefs.dirty()); +} + +TEST(PreferenceTask, IgnoresUnrelatedEvents) { + NiceMock eeprom; + PreferencesStore prefs{eeprom, remember_on()}; + PreferenceTask task{prefs}; + + task.on_event(PpsTargetEvent{1, 9000, 2000}, 0); + task.on_event(EncoderEvent{1}, 0); + + EXPECT_FALSE(prefs.dirty()); +} + +// —— Debounced saving + +TEST(PreferenceTask, CommitsProfileAfterDebounceQuiet) { + NiceMock eeprom; + PreferencesStore prefs{eeprom, remember_on()}; + PreferenceTask task{prefs}; + + EXPECT_CALL(eeprom, save(_)).Times(1).WillOnce([](const Preferences& p) { + EXPECT_TRUE(p.last_profile.is_pps); + EXPECT_EQ(p.last_profile.pdo_index, 1); + EXPECT_EQ(p.last_profile.voltage_mv, 9000); + return true; + }); + + task.on_event(ActiveProfileEvent{true, 1, 9000, 2000}, 0); + task.on_tick(0); // notices the change, starts the timer + task.on_tick(EEPROM_SAVE_DEBOUNCE_MS - 1); // too soon + task.on_tick(EEPROM_SAVE_DEBOUNCE_MS); // fires + task.on_tick(EEPROM_SAVE_DEBOUNCE_MS + 500); // already clean, no second save +} + +TEST(PreferenceTask, NoCommitBeforeDebounce) { + NiceMock eeprom; + PreferencesStore prefs{eeprom, remember_on()}; + PreferenceTask task{prefs}; + + EXPECT_CALL(eeprom, save(_)).Times(0); + + task.on_event(ActiveProfileEvent{true, 1, 9000, 2000}, 0); + task.on_tick(0); + task.on_tick(EEPROM_SAVE_DEBOUNCE_MS - 1); +} + +TEST(PreferenceTask, LaterChangeRestartsDebounce) { + NiceMock eeprom; + PreferencesStore prefs{eeprom, remember_on()}; + PreferenceTask task{prefs}; + + EXPECT_CALL(eeprom, save(_)).Times(0); + + task.on_event(ActiveProfileEvent{true, 1, 9000, 2000}, 0); + task.on_tick(0); + task.on_tick(1500); + task.on_event(ActiveProfileEvent{true, 1, 9500, 2000}, 1500); // new value + task.on_tick(1500); // restamps + task.on_tick(3000); // 1500 < debounce +} + +TEST(PreferenceTask, NoCommitWhenNothingChanged) { + NiceMock eeprom; + PreferencesStore prefs{eeprom}; + PreferenceTask task{prefs}; + + EXPECT_CALL(eeprom, save(_)).Times(0); + + task.on_tick(0); + task.on_tick(EEPROM_SAVE_DEBOUNCE_MS + 5000); +} + +TEST(PreferenceTask, CommitsAnyDirtyField) { + // Not profile-specific: a flip_display_enabled change set directly is persisted too. + NiceMock eeprom; + PreferencesStore prefs{eeprom}; + PreferenceTask task{prefs}; + + EXPECT_CALL(eeprom, save(_)).WillOnce([](const Preferences& p) { + EXPECT_TRUE(p.flip_display_enabled); + return true; + }); + + prefs.set({.flip_display_enabled = true}); + task.on_tick(0); + task.on_tick(EEPROM_SAVE_DEBOUNCE_MS); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/test_v2_resolve/test.cpp b/test/test_v2_resolve/test.cpp new file mode 100644 index 0000000..85fb3f1 --- /dev/null +++ b/test/test_v2_resolve/test.cpp @@ -0,0 +1,137 @@ +#define VERSION "\"test\"" + +#include +#include +#include + +#include "v2/stages/obtain_stage.h" + +using namespace pocketpd; +using ::testing::_; +using ::testing::NiceMock; +using ::testing::Return; + +TEST(ResolveRememberedProfile, RememberOffReturnsFalse) { + NiceMock sink; + EXPECT_CALL(sink, pdo_count()).WillRepeatedly(Return(3)); + + Preferences prefs{.restore_last_profile_enabled = false, .last_profile = {.voltage_mv = 9000}}; + LastProfile out; + EXPECT_FALSE(resolve_profile(prefs, sink, out)); +} + +TEST(ResolveRememberedProfile, FixedExactVoltageResolves) { + NiceMock sink; + EXPECT_CALL(sink, pdo_count()).WillRepeatedly(Return(3)); + EXPECT_CALL(sink, is_index_fixed(_)).WillRepeatedly(Return(false)); + EXPECT_CALL(sink, is_index_fixed(1)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_max_voltage_mv(_)).WillRepeatedly(Return(0)); + EXPECT_CALL(sink, pdo_max_voltage_mv(1)).WillRepeatedly(Return(9000)); + + Preferences prefs{ + .restore_last_profile_enabled = true, + .last_profile = {.is_pps = false, .voltage_mv = 9000, .pdo_index = 1}, + }; + LastProfile out; + ASSERT_TRUE(resolve_profile(prefs, sink, out)); + EXPECT_EQ(out.pdo_index, 1); + EXPECT_EQ(out.voltage_mv, 9000); + EXPECT_EQ(out.current_ma, 0); +} + +TEST(ResolveRememberedProfile, FixedNoMatchingVoltageReturnsFalse) { + NiceMock sink; + EXPECT_CALL(sink, pdo_count()).WillRepeatedly(Return(3)); + EXPECT_CALL(sink, is_index_fixed(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_max_voltage_mv(_)).WillRepeatedly(Return(20000)); + + Preferences prefs{ + .restore_last_profile_enabled = true, + .last_profile = {.is_pps = false, .voltage_mv = 9000}, + }; + LastProfile out; + EXPECT_FALSE(resolve_profile(prefs, sink, out)); +} + +TEST(ResolveRememberedProfile, PpsInRangeResolvesAndClampsCurrent) { + NiceMock sink; + EXPECT_CALL(sink, pdo_count()).WillRepeatedly(Return(3)); + EXPECT_CALL(sink, is_index_pps(_)).WillRepeatedly(Return(false)); + EXPECT_CALL(sink, is_index_pps(2)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_min_voltage_mv(2)).WillRepeatedly(Return(3300)); + EXPECT_CALL(sink, pdo_max_voltage_mv(2)).WillRepeatedly(Return(11000)); + EXPECT_CALL(sink, pdo_max_current_ma(2)).WillRepeatedly(Return(3000)); + + Preferences prefs{ + .restore_last_profile_enabled = true, + .last_profile = {.is_pps = true, .voltage_mv = 9000, .current_ma = 5000, .pdo_index = 2}, + }; + LastProfile out; + ASSERT_TRUE(resolve_profile(prefs, sink, out)); + EXPECT_EQ(out.pdo_index, 2); + EXPECT_EQ(out.voltage_mv, 9000); + EXPECT_EQ(out.current_ma, 3000); // clamped to PDO max +} + +TEST(ResolveRememberedProfile, PpsOutOfRangeReturnsFalse) { + NiceMock sink; + EXPECT_CALL(sink, pdo_count()).WillRepeatedly(Return(3)); + EXPECT_CALL(sink, is_index_pps(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_min_voltage_mv(_)).WillRepeatedly(Return(3300)); + EXPECT_CALL(sink, pdo_max_voltage_mv(_)).WillRepeatedly(Return(11000)); + + Preferences prefs{ + .restore_last_profile_enabled = true, + .last_profile = {.is_pps = true, .voltage_mv = 15000, .current_ma = 2000}, + }; + LastProfile out; + EXPECT_FALSE(resolve_profile(prefs, sink, out)); +} + +TEST(ResolveRememberedProfile, WrongVoltageAtSavedIndexReturnsFalse) { + // Same index on a different charger now holds a different fixed voltage. + NiceMock sink; + EXPECT_CALL(sink, pdo_count()).WillRepeatedly(Return(3)); + EXPECT_CALL(sink, is_index_fixed(1)).WillRepeatedly(Return(true)); + EXPECT_CALL(sink, pdo_max_voltage_mv(1)).WillRepeatedly(Return(20000)); + + Preferences prefs{ + .restore_last_profile_enabled = true, + .last_profile = {.is_pps = false, .voltage_mv = 9000, .pdo_index = 1}, + }; + LastProfile out; + EXPECT_FALSE(resolve_profile(prefs, sink, out)); + EXPECT_EQ(out.voltage_mv, 0); // out untouched on false +} + +TEST(ResolveRememberedProfile, TypeMismatchAtSavedIndexReturnsFalse) { + // Saved a fixed profile, but that index is now a PPS APDO. + NiceMock sink; + EXPECT_CALL(sink, pdo_count()).WillRepeatedly(Return(3)); + EXPECT_CALL(sink, is_index_fixed(1)).WillRepeatedly(Return(false)); + + Preferences prefs{ + .restore_last_profile_enabled = true, + .last_profile = {.is_pps = false, .voltage_mv = 9000, .pdo_index = 1}, + }; + LastProfile out; + EXPECT_FALSE(resolve_profile(prefs, sink, out)); +} + +TEST(ResolveRememberedProfile, IndexBeyondPdoCountReturnsFalse) { + // Charger advertises fewer PDOs than when the profile was saved. + NiceMock sink; + EXPECT_CALL(sink, pdo_count()).WillRepeatedly(Return(2)); + + Preferences prefs{ + .restore_last_profile_enabled = true, + .last_profile = {.is_pps = false, .voltage_mv = 9000, .pdo_index = 5}, + }; + LastProfile out; + EXPECT_FALSE(resolve_profile(prefs, sink, out)); +} + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/test/test_v2_sensor/test.cpp b/test/test_v2_sensor/test.cpp index 4702b8b..f286a59 100644 --- a/test/test_v2_sensor/test.cpp +++ b/test/test_v2_sensor/test.cpp @@ -6,6 +6,8 @@ */ #define VERSION "\"test\"" +#include + #include #include #include @@ -13,8 +15,6 @@ #include #include -#include - #include "v2/app.h" #include "v2/events.h" #include "v2/tasks/sensor_task.h" diff --git a/test/test_v2_settings/test.cpp b/test/test_v2_settings/test.cpp index 16bb1a5..d72ec6f 100644 --- a/test/test_v2_settings/test.cpp +++ b/test/test_v2_settings/test.cpp @@ -10,15 +10,15 @@ #include #include "v2/app.h" -#include "v2/preferences_store.h" #include "v2/events.h" #include "v2/hal/eeprom.h" +#include "v2/preferences_store.h" using namespace pocketpd; +using ::testing::_; using ::testing::NiceMock; using ::testing::Return; using ::testing::StrEq; -using ::testing::_; using TestConductor = App::Conductor; @@ -51,15 +51,15 @@ TEST(SettingsStage, RendersOneRowWithUncheckedBox) { Harness h; EXPECT_CALL(h.display, draw_text(_, _, _)).Times(::testing::AnyNumber()); EXPECT_CALL(h.display, draw_text(0, 12, StrEq(">"))).Times(1); - EXPECT_CALL(h.display, draw_text(10, 12, StrEq("[ ] Skip picker"))).Times(1); + EXPECT_CALL(h.display, draw_text(10, 12, StrEq("[ ] Restore profile"))).Times(1); h.conductor.start(0); } TEST(SettingsStage, RendersCheckedWhenSettingTrue) { Harness h; - h.prefs.set({.skip_picker_on_boot = true}); + h.prefs.set({.restore_last_profile_enabled = true}); EXPECT_CALL(h.display, draw_text(_, _, _)).Times(::testing::AnyNumber()); - EXPECT_CALL(h.display, draw_text(10, 12, StrEq("[X] Skip picker"))).Times(1); + EXPECT_CALL(h.display, draw_text(10, 12, StrEq("[X] Restore profile"))).Times(1); h.conductor.start(0); } @@ -70,7 +70,7 @@ TEST(SettingsStage, EncoderLongTogglesInRamWithoutSaving) { EXPECT_CALL(h.eeprom, save(_)).Times(0); h.stage.on_event(h.conductor, ButtonEvent{ButtonId::ENCODER, Gesture::LONG}, 0); - EXPECT_TRUE(h.prefs.get().skip_picker_on_boot); + EXPECT_TRUE(h.prefs.get().restore_last_profile_enabled); EXPECT_TRUE(h.prefs.dirty()); } @@ -82,11 +82,10 @@ TEST(SettingsStage, ExitFlushesPendingToggleToEeprom) { h.stage.on_event(h.conductor, ButtonEvent{ButtonId::ENCODER, Gesture::LONG}, 0); - EXPECT_CALL(h.eeprom, save(_)) - .WillOnce([](const Preferences& s) { - EXPECT_TRUE(s.skip_picker_on_boot); - return true; - }); + EXPECT_CALL(h.eeprom, save(_)).WillOnce([](const Preferences& s) { + EXPECT_TRUE(s.restore_last_profile_enabled); + return true; + }); h.stage.on_event(h.conductor, ButtonEvent{ButtonId::L, Gesture::LONG}, 0); EXPECT_TRUE(h.conductor.apply_pending_transition(0)); @@ -103,7 +102,7 @@ TEST(SettingsStage, MultipleTogglesCollapseToSingleSaveOnExit) { h.stage.on_event(h.conductor, ButtonEvent{ButtonId::ENCODER, Gesture::LONG}, 0); h.stage.on_event(h.conductor, ButtonEvent{ButtonId::ENCODER, Gesture::LONG}, 0); h.stage.on_event(h.conductor, ButtonEvent{ButtonId::ENCODER, Gesture::LONG}, 0); - EXPECT_TRUE(h.prefs.get().skip_picker_on_boot); + EXPECT_TRUE(h.prefs.get().restore_last_profile_enabled); h.stage.on_event(h.conductor, ButtonEvent{ButtonId::L, Gesture::LONG}, 0); EXPECT_TRUE(h.conductor.apply_pending_transition(0)); @@ -125,7 +124,7 @@ TEST(SettingsStage, ExitWithoutTogglesDoesNotSave) { TEST(SettingsStage, RendersVoltageCompRowBelowSkipPicker) { Harness h; EXPECT_CALL(h.display, draw_text(_, _, _)).Times(::testing::AnyNumber()); - EXPECT_CALL(h.display, draw_text(10, 12, StrEq("[ ] Skip picker"))).Times(1); + EXPECT_CALL(h.display, draw_text(10, 12, StrEq("[ ] Restore profile"))).Times(1); EXPECT_CALL(h.display, draw_text(10, 24, StrEq("[ ] Voltage comp"))).Times(1); h.conductor.start(0); } @@ -149,7 +148,7 @@ TEST(SettingsStage, EncoderLongOnVoltageCompTogglesPreference) { h.stage.on_event(h.conductor, EncoderEvent{1}, 0); h.stage.on_event(h.conductor, ButtonEvent{ButtonId::ENCODER, Gesture::LONG}, 0); - EXPECT_TRUE(h.prefs.get().voltage_comp_enabled); + EXPECT_TRUE(h.prefs.get().voltage_compensate_enabled); EXPECT_TRUE(h.prefs.dirty()); } @@ -162,12 +161,11 @@ TEST(SettingsStage, ExitFlushesVoltageCompToggle) { h.stage.on_event(h.conductor, EncoderEvent{1}, 0); h.stage.on_event(h.conductor, ButtonEvent{ButtonId::ENCODER, Gesture::LONG}, 0); - EXPECT_CALL(h.eeprom, save(_)) - .WillOnce([](const Preferences& s) { - EXPECT_TRUE(s.voltage_comp_enabled); - EXPECT_FALSE(s.skip_picker_on_boot); - return true; - }); + EXPECT_CALL(h.eeprom, save(_)).WillOnce([](const Preferences& s) { + EXPECT_TRUE(s.voltage_compensate_enabled); + EXPECT_FALSE(s.restore_last_profile_enabled); + return true; + }); h.stage.on_event(h.conductor, ButtonEvent{ButtonId::L, Gesture::LONG}, 0); EXPECT_TRUE(h.conductor.apply_pending_transition(0)); @@ -188,7 +186,7 @@ TEST(SettingsStage, EncoderLongOnFlipDisplayTogglesPreferenceAndApplies) { h.stage.on_event(h.conductor, EncoderEvent{1}, 0); h.stage.on_event(h.conductor, ButtonEvent{ButtonId::ENCODER, Gesture::LONG}, 0); - EXPECT_TRUE(h.prefs.get().flip_display); + EXPECT_TRUE(h.prefs.get().flip_display_enabled); EXPECT_TRUE(h.prefs.dirty()); EXPECT_TRUE(h.orientation.flipped()); EXPECT_EQ(h.orientation.call_count(), 1); @@ -203,7 +201,7 @@ TEST(SettingsStage, FlipDisplayToggleTwiceRestoresOrientation) { h.stage.on_event(h.conductor, ButtonEvent{ButtonId::ENCODER, Gesture::LONG}, 0); h.stage.on_event(h.conductor, ButtonEvent{ButtonId::ENCODER, Gesture::LONG}, 0); - EXPECT_FALSE(h.prefs.get().flip_display); + EXPECT_FALSE(h.prefs.get().flip_display_enabled); EXPECT_FALSE(h.orientation.flipped()); EXPECT_EQ(h.orientation.call_count(), 2); } From b0cc3a748f1f753f6585b952d983f8bc01e9d21b Mon Sep 17 00:00:00 2001 From: Kawin Pechetratanapanit <39807451+kawinie@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:37:51 -0700 Subject: [PATCH 2/2] style: clang-format test suites --- test/test_AP33772/test.cpp | 13 ++++++------- test/test_v2_command/test.cpp | 6 +++--- test/test_v2_energy/test.cpp | 12 ++++++++---- test/test_v2_menu/test.cpp | 4 ++-- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/test/test_AP33772/test.cpp b/test/test_AP33772/test.cpp index 6ad6810..e494b16 100644 --- a/test/test_AP33772/test.cpp +++ b/test/test_AP33772/test.cpp @@ -6,13 +6,12 @@ * Uses GMock MockI2CDevice (scripted reads + EXPECT_CALL assertions on writes) * and a no-op delay function (no real sleep). */ -#include -#include - #include -#include #include +#include +#include +#include using namespace ap33772; using ::testing::_; @@ -250,9 +249,9 @@ TEST_F(AP33772Test, FailedPPSRequestDoesNotMutateActiveState) { // Second write: PPS RDO fails. State must stay on PDO 0 / fixed. // Third write: set_current rebuilds for active=PDO 0 (fixed RDO). EXPECT_CALL(m_i2c_device, write_bytes(reg::RDO, _, 4)) - .WillOnce(Return(true)) // set_pdo(0) - .WillOnce(Return(false)) // set_pps_pdo(1, ...) fails - .WillOnce(Return(true)); // set_current(...) — must use fixed path + .WillOnce(Return(true)) // set_pdo(0) + .WillOnce(Return(false)) // set_pps_pdo(1, ...) fails + .WillOnce(Return(true)); // set_current(...) — must use fixed path AP33772 ap(m_i2c_device, noop_delay); EXPECT_TRUE(ap.begin()); diff --git a/test/test_v2_command/test.cpp b/test/test_v2_command/test.cpp index d880be5..df242c1 100644 --- a/test/test_v2_command/test.cpp +++ b/test/test_v2_command/test.cpp @@ -10,15 +10,15 @@ */ #define VERSION "\"test\"" +#include +#include + #include #include #include #include #include -#include -#include - #include "v2/app.h" #include "v2/tasks/command_task.h" diff --git a/test/test_v2_energy/test.cpp b/test/test_v2_energy/test.cpp index 03c261b..e46b910 100644 --- a/test/test_v2_energy/test.cpp +++ b/test/test_v2_energy/test.cpp @@ -347,11 +347,14 @@ TEST(EnergyView, LockedRendersPadlock) { EXPECT_CALL( display, draw_xbm( - EnergyView::PADLOCK_X, EnergyView::PADLOCK_Y, - EnergyView::PADLOCK_W, EnergyView::PADLOCK_H, + EnergyView::PADLOCK_X, + EnergyView::PADLOCK_Y, + EnergyView::PADLOCK_W, + EnergyView::PADLOCK_H, bitmap::PADLOCK.data() ) - ).Times(1); + ) + .Times(1); EnergyViewModel vm{}; vm.output_enabled = false; @@ -367,7 +370,8 @@ TEST(EnergyView, UnlockedDoesNotDrawPadlock) { EXPECT_CALL( display, draw_xbm(_, _, EnergyView::PADLOCK_W, EnergyView::PADLOCK_H, bitmap::PADLOCK.data()) - ).Times(0); + ) + .Times(0); EnergyViewModel vm{}; vm.output_enabled = false; diff --git a/test/test_v2_menu/test.cpp b/test/test_v2_menu/test.cpp index 948f3d9..b474d1f 100644 --- a/test/test_v2_menu/test.cpp +++ b/test/test_v2_menu/test.cpp @@ -10,15 +10,15 @@ #include #include "v2/app.h" -#include "v2/preferences_store.h" #include "v2/events.h" +#include "v2/preferences_store.h" using namespace pocketpd; +using ::testing::_; using ::testing::InSequence; using ::testing::NiceMock; using ::testing::Return; using ::testing::StrEq; -using ::testing::_; using TestConductor = App::Conductor;