mirror of
https://github.com/ALLFATHER-BV/wadamesh.git
synced 2026-11-07 18:37:15 +00:00
test/test_clock_floor.cpp runs src/helpers/ClockFloorRTC.h over a settable hardware clock (test/clock_floor_fakes) with settimeofday / gettimeofday faked, so the machine's clock is never touched. A chip behind the floor or asserting 2043 gives way to the system clock when that is ahead and plausible, else the floor or the core's seed as before; a negative or implausible system clock is never used; a healthy chip whose write takes is followed; a 20000-step random walk of both clocks never steps back or past the plausible window; garbage sets are refused, a >10 min backward set lowers the floor, a poisoned persisted floor is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
118 lines
5.1 KiB
C++
118 lines
5.1 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
// Host test for the clock floor (src/helpers/ClockFloorRTC.h), with #756's change:
|
|
// a hardware clock that reads behind the floor or in the far future gives way to the
|
|
// system clock when that is ahead and plausible (T-Display P4 backups named "nodate"
|
|
// after a good Wi-Fi sync, #746). The core's AutoDiscoverRTCClock is a settable fake
|
|
// (test/clock_floor_fakes) and settimeofday / gettimeofday are faked, so the test
|
|
// never touches the machine's clock.
|
|
//
|
|
// c++ -std=c++17 -Wall -Wextra -I src -I test/clock_floor_fakes test/test_clock_floor.cpp \
|
|
// -o /tmp/test_clock_floor && /tmp/test_clock_floor
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <sys/time.h>
|
|
|
|
static long g_sys = 0; // the system clock, seconds
|
|
static int g_sys_sets = 0;
|
|
static int fakeSetTimeOfDay(const struct timeval* tv, const void*) { g_sys = tv->tv_sec; ++g_sys_sets; return 0; }
|
|
static int fakeGetTimeOfDay(struct timeval* tv, void*) { tv->tv_sec = g_sys; tv->tv_usec = 0; return 0; }
|
|
#define settimeofday fakeSetTimeOfDay
|
|
#define gettimeofday fakeGetTimeOfDay
|
|
#include "helpers/ClockFloorRTC.h"
|
|
#undef settimeofday
|
|
#undef gettimeofday
|
|
|
|
struct NullClock : mesh::RTCClock {
|
|
uint32_t getCurrentTime() override { return 0; }
|
|
void setCurrentTime(uint32_t) override {}
|
|
};
|
|
|
|
using C = ClockFloorRTC;
|
|
static const uint32_t MIN = C::MIN_VALID_EPOCH, MAX = C::MAX_PLAUSIBLE_EPOCH;
|
|
static const uint32_t NOW = 1790000000u; // 21 Sep 2026: valid, below MAX
|
|
static const uint32_t FUTURE_2043 = 2324000000u;
|
|
|
|
struct Rig {
|
|
NullClock fb;
|
|
C clk{fb};
|
|
AutoDiscoverRTCClock& hw() { return static_cast<AutoDiscoverRTCClock&>(clk); }
|
|
};
|
|
|
|
int main() {
|
|
assert(MIN < NOW && NOW < MAX && MAX < FUTURE_2043);
|
|
|
|
// A healthy chip ahead of the floor: its time, the floor follows.
|
|
{ Rig r; g_sys = 0; r.hw().hw = NOW; assert(r.clk.getCurrentTime() == NOW && r.clk.getFloor() == NOW); }
|
|
|
|
// #756: the chip reads BEHIND the floor (stopped oscillator, a write that did not
|
|
// take) while the system clock runs ahead: the system clock wins and lifts the floor.
|
|
{ Rig r; r.clk.seedFloor(NOW); r.hw().hw = NOW - 3600; g_sys = NOW + 120;
|
|
assert(r.clk.getCurrentTime() == NOW + 120 && r.clk.getFloor() == NOW + 120); }
|
|
// ...behind the floor, and the system clock is not ahead: hold the floor (as before)
|
|
{ Rig r; r.clk.seedFloor(NOW); r.hw().hw = NOW - 3600; g_sys = NOW - 10;
|
|
assert(r.clk.getCurrentTime() == NOW); }
|
|
// ...behind the floor, system clock on an implausible value: hold the floor
|
|
{ Rig r; r.clk.seedFloor(NOW); r.hw().hw = NOW - 3600; g_sys = FUTURE_2043;
|
|
assert(r.clk.getCurrentTime() == NOW); }
|
|
{ Rig r; r.clk.seedFloor(NOW); r.hw().hw = NOW - 3600; g_sys = -5; // gettimeofday < 0
|
|
assert(r.clk.getCurrentTime() == NOW); }
|
|
|
|
// A chip asserting 2043: the system clock when it is ahead and plausible...
|
|
{ Rig r; r.clk.seedFloor(NOW); r.hw().hw = FUTURE_2043; g_sys = NOW + 60;
|
|
assert(r.clk.getCurrentTime() == NOW + 60); }
|
|
// ...else the floor, or the core's unset seed when there is no floor yet (as before)
|
|
{ Rig r; r.clk.seedFloor(NOW); r.hw().hw = FUTURE_2043; g_sys = MIN - 100;
|
|
assert(r.clk.getCurrentTime() == NOW); }
|
|
{ Rig r; r.hw().hw = FUTURE_2043; g_sys = MIN; // the system clock on the seed is not "ahead"
|
|
assert(r.clk.getCurrentTime() == MIN); }
|
|
{ Rig r; r.hw().hw = FUTURE_2043; g_sys = 0;
|
|
assert(r.clk.getCurrentTime() == MIN && r.clk.getFloor() == 0); }
|
|
|
|
// A healthy chip whose write takes: after a set it reads the new time and is used.
|
|
{
|
|
Rig r; r.hw().hw = MIN; g_sys = MIN;
|
|
r.clk.setCurrentTime(NOW);
|
|
assert(r.hw().hw == NOW && r.clk.getCurrentTime() == NOW);
|
|
r.hw().hw = NOW + 5; g_sys = NOW + 4; // the chip a second ahead of the system clock
|
|
assert(r.clk.getCurrentTime() == NOW + 5);
|
|
}
|
|
|
|
// Monotonic whatever the two clocks do: a random walk of both never makes it step back.
|
|
{
|
|
Rig r; r.clk.seedFloor(NOW);
|
|
uint32_t x = 12345, last = 0;
|
|
for (int i = 0; i < 20000; ++i) {
|
|
x = x * 1664525u + 1013904223u;
|
|
r.hw().hw = (x & 1) ? NOW + (x >> 20) : ((x & 2) ? FUTURE_2043 : NOW - (x >> 20));
|
|
g_sys = (x & 4) ? (long)(NOW + ((x >> 8) & 0xFFFF)) : (long)(MIN - 1);
|
|
const uint32_t t = r.clk.getCurrentTime();
|
|
assert(t >= last && t <= MAX);
|
|
last = t;
|
|
}
|
|
}
|
|
|
|
// setCurrentTime: garbage sets are refused (no chip write, no system clock set)
|
|
{
|
|
Rig r; g_sys_sets = 0;
|
|
r.clk.setCurrentTime(MIN); // the seed itself: the "Sync from system" poisoning
|
|
r.clk.setCurrentTime(1000); // 1970-class
|
|
r.clk.setCurrentTime(FUTURE_2043);
|
|
assert(r.hw().writes == 0 && g_sys_sets == 0 && !r.clk.timeIsCurrent());
|
|
}
|
|
// a >10 min backward correction pulls the floor down; a smaller one keeps it
|
|
{
|
|
Rig r; r.clk.seedFloor(NOW + 3600);
|
|
r.clk.setCurrentTime(NOW);
|
|
assert(r.clk.getFloor() == NOW);
|
|
Rig q; q.clk.seedFloor(NOW + 300);
|
|
q.clk.setCurrentTime(NOW);
|
|
assert(q.clk.getFloor() == NOW + 300);
|
|
}
|
|
// a poisoned persisted floor is refused
|
|
{ Rig r; r.clk.seedFloor(FUTURE_2043); assert(r.clk.getFloor() == 0); }
|
|
|
|
printf("test_clock_floor: ok\n");
|
|
return 0;
|
|
}
|