Public Access
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
128 lines
3.4 KiB
C++
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();
|
|
}
|