Files
roro9stack/test/test_service_manager/test_service_manager.cpp
T
twislaandClaude Opus 5.5 ae0726b7f9 Add core runtime: event bus, services, app lifecycle
lib/core is hardware-independent and unit-tested on the host:
- EventBus: fixed-size thread-safe queue, delivered on the UI task by
  dispatch(); drops newest when full and counts drops
- Service / ServiceManager: cooperative ticking at per-service intervals,
  no catch-up bursts, safe across millis() wraparound
- App / AppManager: one foreground App, Home always to Launcher, Back
  offered to the App first, hidden Apps, redraw requests
- KeyEvent: logical keys for the upcoming input layer

Firmware main loop now ticks services and dispatches events.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
2026-10-02 09:10:10 +02:00

128 lines
3.4 KiB
C++

#include <unity.h>
#include <string>
#include <vector>
#include "service_manager.h"
using namespace roro;
void setUp() {}
void tearDown() {}
struct FakeService : Service {
FakeService(const char* n, uint32_t interval, std::vector<std::string>* log)
: n_(n), interval_(interval), log_(log) {}
const char* name() const override { return n_; }
uint32_t tickIntervalMs() const override { return interval_; }
void start() override { log_->push_back(std::string("start ") + n_); }
void stop() override { log_->push_back(std::string("stop ") + n_); }
void tick(uint32_t nowMs) override {
ticks.push_back(nowMs);
log_->push_back(std::string("tick ") + n_);
}
std::vector<uint32_t> ticks;
private:
const char* n_;
uint32_t interval_;
std::vector<std::string>* log_;
};
void test_services_start_in_order_and_stop_in_reverse() {
std::vector<std::string> log;
FakeService a("a", 100, &log), b("b", 100, &log);
ServiceManager m;
m.add(a);
m.add(b);
m.startAll(0);
m.stopAll();
std::vector<std::string> expected = {"start a", "start b", "stop b", "stop a"};
TEST_ASSERT_TRUE(log == expected);
}
void test_service_ticks_immediately_after_start_then_at_its_interval() {
std::vector<std::string> log;
FakeService s("s", 100, &log);
ServiceManager m;
m.add(s);
m.startAll(0);
for (uint32_t t = 0; t <= 250; t += 10) m.tick(t);
TEST_ASSERT_EQUAL(3, s.ticks.size());
TEST_ASSERT_EQUAL(0, s.ticks[0]);
TEST_ASSERT_EQUAL(100, s.ticks[1]);
TEST_ASSERT_EQUAL(200, s.ticks[2]);
}
void test_each_service_keeps_its_own_interval() {
std::vector<std::string> log;
FakeService fast("fast", 10, &log), slow("slow", 100, &log);
ServiceManager m;
m.add(fast);
m.add(slow);
m.startAll(0);
for (uint32_t t = 0; t < 100; t += 10) m.tick(t);
TEST_ASSERT_EQUAL(10, fast.ticks.size());
TEST_ASSERT_EQUAL(1, slow.ticks.size());
}
void test_late_tick_does_not_cause_a_burst_of_catch_up_ticks() {
std::vector<std::string> log;
FakeService s("s", 100, &log);
ServiceManager m;
m.add(s);
m.startAll(0);
m.tick(0);
m.tick(1000); // UI stalled for a second
m.tick(1010);
TEST_ASSERT_EQUAL(2, s.ticks.size());
}
void test_ticking_survives_millis_wraparound() {
std::vector<std::string> log;
FakeService s("s", 100, &log);
ServiceManager m;
m.add(s);
const uint32_t nearWrap = 0xFFFFFFFFu - 50;
m.startAll(nearWrap);
m.tick(nearWrap);
m.tick(nearWrap + 60); // wrapped, only 60 ms elapsed
m.tick(nearWrap + 100); // wrapped, 100 ms elapsed
TEST_ASSERT_EQUAL(2, s.ticks.size());
}
void test_services_are_not_ticked_before_start_or_after_stop() {
std::vector<std::string> log;
FakeService s("s", 10, &log);
ServiceManager m;
m.add(s);
m.tick(0);
m.startAll(0);
m.stopAll();
m.tick(100);
TEST_ASSERT_EQUAL(0, s.ticks.size());
}
int main() {
UNITY_BEGIN();
RUN_TEST(test_services_start_in_order_and_stop_in_reverse);
RUN_TEST(test_service_ticks_immediately_after_start_then_at_its_interval);
RUN_TEST(test_each_service_keeps_its_own_interval);
RUN_TEST(test_late_tick_does_not_cause_a_burst_of_catch_up_ticks);
RUN_TEST(test_ticking_survives_millis_wraparound);
RUN_TEST(test_services_are_not_ticked_before_start_or_after_stop);
return UNITY_END();
}