Files
MeshCore/test/fixtures/mono_led_rollover.cpp
T

206 lines
6.7 KiB
C++

// Full actual handlers/field initializers are injected. RGB is not configured.
// Arduino clock and output writes are the only hardware boundaries mocked.
#include <helpers/CompanionNotificationPolicy.h>
#include <helpers/LedPolicy.h>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@CONSTANTS@
static uint32_t clock_ms = 0;
uint32_t millis() { return clock_ms; }
static unsigned writes = 0;
static const char* active_case = "startup";
static unsigned checks = 0;
#define CHECK(value) do { \
++checks; \
if (!(value)) { \
std::fprintf(stderr, "FAIL %s: %s (line %d)\n", active_case, #value, __LINE__); \
std::exit(1); \
} \
} while (0)
#ifdef PIN_STATUS_LED
static unsigned last_level = 9;
void digitalWrite(unsigned pin, unsigned level) {
CHECK(pin == PIN_STATUS_LED);
CHECK(level <= 1);
++writes;
last_level = level;
}
#endif
class UITask {
public:
@DEFAULTS@
@FIELDS@
@SETTERS@
int _msgcount = 0;
void userLedHandler();
};
@HANDLER@
#ifdef PIN_STATUS_LED
static void at(UITask& ui, uint32_t now, unsigned count, bool on) {
clock_ms = now;
ui.userLedHandler();
CHECK(writes == count);
if (count) CHECK(last_level == unsigned(on ? LED_STATE_ON : !LED_STATE_ON));
}
static void cadence(UITask& ui, uint32_t start, uint32_t pulse) {
at(ui, start, 1, true);
at(ui, start, 1, true);
at(ui, start + pulse, 1, true);
at(ui, start + pulse + 1, 2, false);
at(ui, start + pulse + 1, 2, false);
at(ui, start + LED_CYCLE_MILLIS + 1, 2, false);
at(ui, start + LED_CYCLE_MILLIS + 2, 3, true);
}
static void suppressed(UITask& ui, bool owner, bool initial_on) {
constexpr uint32_t start = 100;
constexpr uint32_t inactive = 26u * 24u * 60u * 60u * 1000u;
at(ui, start, 1, true);
if (!initial_on) at(ui, start + LED_ON_MILLIS + 1, 2, false);
const unsigned before = writes;
const uint32_t suppressed_at = clock_ms;
if (owner) ui.setNotificationLedOwnership(true);
else ui.setNotificationOutputs(uint8_t(31 & ~mesh::notify::Led));
at(ui, suppressed_at + 1, before, initial_on);
at(ui, suppressed_at + inactive, before, initial_on);
if (owner) ui.setNotificationLedOwnership(false);
else ui.setNotificationOutputs(31);
at(ui, suppressed_at + inactive, before + 1, !initial_on);
at(ui, suppressed_at + inactive, before + 1, !initial_on);
const uint32_t phase = initial_on ? LED_CYCLE_MILLIS - LED_ON_MILLIS : LED_ON_MILLIS;
at(ui, suppressed_at + inactive + phase, before + 1, !initial_on);
at(ui, suppressed_at + inactive + phase + 1, before + 2, initial_on);
}
#endif
// A new process per case preserves real ui-orig function-static initialization.
int main(int argc, char** argv) {
CHECK(argc == 2);
active_case = argv[1];
UITask ui;
CHECK(mesh::leds::enabled());
const char* name = argv[1];
#ifdef PIN_STATUS_LED
if (!std::strcmp(name, "quiet-fresh")) {
mesh::leds::setEnabled(false);
at(ui, 0, 0, false);
at(ui, 0x80000000u, 0, false);
mesh::leds::setEnabled(true);
cadence(ui, 0x80000000u, LED_ON_MILLIS);
} else if (!std::strcmp(name, "zero")) {
at(ui, 0, 0, false);
cadence(ui, 1, LED_ON_MILLIS);
} else if (!std::strcmp(name, "upper")) {
cadence(ui, 0x80000000u, LED_ON_MILLIS);
} else if (!std::strcmp(name, "max")) {
cadence(ui, UINT32_MAX, LED_ON_MILLIS);
} else if (!std::strcmp(name, "normal")) {
cadence(ui, 100, LED_ON_MILLIS);
} else if (!std::strcmp(name, "unread")) {
ui._msgcount = 1;
cadence(ui, 100, LED_ON_MSG_MILLIS);
} else if (!std::strcmp(name, "signed")) {
cadence(ui, 0x7fffffffu - 10, LED_ON_MILLIS);
} else if (!std::strcmp(name, "wrap")) {
cadence(ui, UINT32_MAX - 10, LED_ON_MILLIS);
} else if (!std::strcmp(name, "off-wrap")) {
cadence(ui, UINT32_MAX - 100, LED_ON_MILLIS);
} else if (!std::strcmp(name, "zero-stamp")) {
at(ui, uint32_t(0u - LED_ON_MILLIS - 1u), 1, true);
at(ui, UINT32_MAX, 1, true);
at(ui, 0, 2, false);
at(ui, 0, 2, false);
at(ui, LED_CYCLE_MILLIS - LED_ON_MILLIS, 2, false);
at(ui, LED_CYCLE_MILLIS - LED_ON_MILLIS + 1, 3, true);
} else if (!std::strcmp(name, "message-on")) {
ui._msgcount = 2;
at(ui, 100, 1, true);
ui._msgcount = 0;
at(ui, 120, 1, true);
at(ui, 300, 1, true);
at(ui, 301, 2, false);
at(ui, 4101, 2, false);
at(ui, 4102, 3, true);
at(ui, 4122, 3, true);
at(ui, 4123, 4, false);
} else if (!std::strcmp(name, "message-off")) {
at(ui, 100, 1, true);
at(ui, 121, 2, false);
ui._msgcount = 1;
at(ui, 130, 2, false);
at(ui, 4101, 2, false);
at(ui, 4102, 3, true);
at(ui, 4302, 3, true);
at(ui, 4303, 4, false);
} else if (!std::strcmp(name, "owner-fresh") || !std::strcmp(name, "mask-fresh")) {
const bool owner = !std::strcmp(name, "owner-fresh");
if (owner) ui.setNotificationLedOwnership(true);
else ui.setNotificationOutputs(uint8_t(31 & ~mesh::notify::Led));
at(ui, 0, 0, false);
at(ui, 0x80000000u, 0, false);
if (owner) ui.setNotificationLedOwnership(false);
else ui.setNotificationOutputs(31);
cadence(ui, 0x80000000u, LED_ON_MILLIS);
} else if (!std::strcmp(name, "owner-on-long")) {
suppressed(ui, true, true);
} else if (!std::strcmp(name, "owner-off-long")) {
suppressed(ui, true, false);
} else if (!std::strcmp(name, "mask-on-long")) {
suppressed(ui, false, true);
} else if (!std::strcmp(name, "mask-off-long")) {
suppressed(ui, false, false);
} else if (!std::strcmp(name, "suppressed-short")) {
at(ui, 100, 1, true);
ui.setNotificationLedOwnership(true);
at(ui, 105, 1, true);
ui.setNotificationLedOwnership(false);
at(ui, 110, 1, true);
at(ui, 120, 1, true);
at(ui, 121, 2, false);
} else if (!std::strcmp(name, "delayed-one-phase")) {
at(ui, 100, 1, true);
at(ui, 100100, 2, false);
at(ui, 100100, 2, false);
at(ui, 104080, 2, false);
at(ui, 104081, 3, true);
} else if (!std::strcmp(name, "both-vetoes")) {
ui.setNotificationLedOwnership(true);
ui.setNotificationOutputs(0);
at(ui, 100, 0, false);
ui.setNotificationLedOwnership(false);
at(ui, 200, 0, false);
ui.setNotificationOutputs(31);
at(ui, 200, 1, true);
} else {
std::fprintf(stderr, "unknown case: %s\n", name);
return 2;
}
#else
CHECK(std::strcmp(name, "no-led") == 0);
const uint32_t stamps[] = {0u, 1u, 0x7fffffffu, 0x80000000u, UINT32_MAX};
for (uint32_t stamp : stamps) {
clock_ms = stamp;
ui.userLedHandler();
CHECK(writes == 0);
ui.setNotificationLedOwnership(true);
ui.userLedHandler();
CHECK(writes == 0);
ui.setNotificationLedOwnership(false);
ui.setNotificationOutputs(0);
ui.userLedHandler();
CHECK(writes == 0);
ui.setNotificationOutputs(31);
}
#endif
std::printf("PASS %s: %u checks\n", name, checks);
}