mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-08-17 00:00:10 +00:00
660 lines
29 KiB
C++
660 lines
29 KiB
C++
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
#include <limits>
|
|
|
|
#include "Telemetry/LocationShareScheduler.h"
|
|
|
|
namespace {
|
|
|
|
int passed = 0;
|
|
int failures = 0;
|
|
|
|
#define CHECK(expr) \
|
|
do { \
|
|
if (expr) { \
|
|
++passed; \
|
|
} else { \
|
|
++failures; \
|
|
std::cerr << "FAIL line " << __LINE__ << ": " #expr << '\n'; \
|
|
} \
|
|
} while (false)
|
|
|
|
Telemetry::PeerId peer(uint8_t seed) {
|
|
Telemetry::PeerId id{};
|
|
for (std::size_t index = 0; index < Telemetry::PEER_ID_SIZE; ++index) {
|
|
id.bytes[index] = static_cast<uint8_t>(seed + index);
|
|
}
|
|
return id;
|
|
}
|
|
|
|
Telemetry::SharePollResult poll(
|
|
Telemetry::LocationShareScheduler& scheduler,
|
|
uint64_t wall_millis,
|
|
bool location_valid,
|
|
Telemetry::ShareWork& work) {
|
|
return scheduler.poll(wall_millis, wall_millis, location_valid, work);
|
|
}
|
|
|
|
Telemetry::ShareAckResult acknowledge(
|
|
Telemetry::LocationShareScheduler& scheduler,
|
|
const Telemetry::PeerId& id,
|
|
uint64_t token,
|
|
bool accepted,
|
|
uint64_t wall_millis) {
|
|
return scheduler.acknowledge(
|
|
id, token, accepted, wall_millis, wall_millis);
|
|
}
|
|
|
|
Telemetry::ShareStartOptions options(
|
|
Telemetry::ShareDuration duration = Telemetry::ShareDuration::MINUTES_15,
|
|
uint32_t cadence = 60000) {
|
|
Telemetry::ShareStartOptions value{};
|
|
value.duration = duration;
|
|
value.cadence_millis = cadence;
|
|
value.approx_radius_meters = 0;
|
|
return value;
|
|
}
|
|
|
|
void defaultsToNoSharing() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
CHECK(scheduler.size() == 0);
|
|
Telemetry::ShareWork work{};
|
|
CHECK(poll(scheduler, 1000, true, work) == Telemetry::SharePollResult::NO_WORK);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(!scheduler.get(peer(1), session));
|
|
}
|
|
|
|
void revisionsTrackOnlySerializedSessionMutations() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
const auto id = peer(21);
|
|
CHECK(scheduler.revision() == 0);
|
|
CHECK(scheduler.start(id, options(Telemetry::ShareDuration::INDEFINITE), 1000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
CHECK(scheduler.revision() == 1);
|
|
|
|
Telemetry::ShareWork work{};
|
|
CHECK(scheduler.poll(1000, 5000, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(scheduler.revision() == 1);
|
|
CHECK(scheduler.acknowledge(id, work.token, false, 1000, 5001) ==
|
|
Telemetry::ShareAckResult::RETRY_SCHEDULED);
|
|
CHECK(scheduler.revision() == 1);
|
|
CHECK(scheduler.stop(id, 1001) == Telemetry::ShareSessionResult::STOPPING);
|
|
CHECK(scheduler.revision() == 2);
|
|
CHECK(scheduler.stop(id, 1002) == Telemetry::ShareSessionResult::STOPPING);
|
|
CHECK(scheduler.revision() == 2);
|
|
|
|
CHECK(scheduler.poll(1002, 5002, false, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(scheduler.revision() == 2);
|
|
CHECK(scheduler.acknowledge(id, work.token, true, 1002, 5003) ==
|
|
Telemetry::ShareAckResult::CEASED);
|
|
CHECK(scheduler.revision() == 3);
|
|
CHECK(scheduler.stop(id, 1003) == Telemetry::ShareSessionResult::NOT_FOUND);
|
|
CHECK(scheduler.revision() == 3);
|
|
|
|
Telemetry::LocationShareScheduler expiry;
|
|
CHECK(expiry.start(peer(22), options(), 1000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
const uint64_t before_expiry = expiry.revision();
|
|
Telemetry::ShareSession session{};
|
|
CHECK(expiry.get(peer(22), session));
|
|
CHECK(expiry.poll(session.expires_at_millis, 1, false, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(expiry.revision() == before_expiry + 1);
|
|
CHECK(expiry.poll(session.expires_at_millis, 2, false, work) ==
|
|
Telemetry::SharePollResult::NO_WORK);
|
|
CHECK(expiry.revision() == before_expiry + 1);
|
|
}
|
|
|
|
void computesDurationAndMidnightBoundaries() {
|
|
constexpr uint64_t now = 1700000000000ULL;
|
|
struct Case {
|
|
Telemetry::ShareDuration duration;
|
|
uint64_t delta;
|
|
};
|
|
const Case cases[] = {
|
|
{Telemetry::ShareDuration::MINUTES_15, 15ULL * 60ULL * 1000ULL},
|
|
{Telemetry::ShareDuration::HOUR_1, 60ULL * 60ULL * 1000ULL},
|
|
{Telemetry::ShareDuration::HOURS_4, 4ULL * 60ULL * 60ULL * 1000ULL},
|
|
};
|
|
for (const auto& item : cases) {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
CHECK(scheduler.start(peer(1), options(item.duration), now) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(1), session));
|
|
CHECK(session.has_expiry);
|
|
CHECK(session.expires_at_millis == now + item.delta);
|
|
}
|
|
|
|
Telemetry::LocationShareScheduler indefinite;
|
|
CHECK(indefinite.start(peer(2), options(Telemetry::ShareDuration::INDEFINITE), now) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(indefinite.get(peer(2), session));
|
|
CHECK(!session.has_expiry);
|
|
|
|
uint64_t midnight = 0;
|
|
CHECK(Telemetry::nextFixedOffsetMidnight(1704065400000ULL, 3600, midnight) ==
|
|
Telemetry::MidnightResult::OK);
|
|
CHECK(midnight == 1704150000000ULL); // 2024-01-02 00:00 at UTC+1
|
|
CHECK(Telemetry::nextFixedOffsetMidnight(midnight, 3600, midnight) ==
|
|
Telemetry::MidnightResult::OK);
|
|
CHECK(midnight == 1704236400000ULL);
|
|
CHECK(Telemetry::nextFixedOffsetMidnight(0, 0, midnight) ==
|
|
Telemetry::MidnightResult::CLOCK_UNAVAILABLE);
|
|
CHECK(Telemetry::nextFixedOffsetMidnight(1000, 19 * 60 * 60, midnight) ==
|
|
Telemetry::MidnightResult::INVALID_ARGUMENT);
|
|
|
|
auto midnight_options = options(Telemetry::ShareDuration::LOCAL_MIDNIGHT);
|
|
midnight_options.local_midnight_millis = 1704150000000ULL;
|
|
Telemetry::LocationShareScheduler at_midnight;
|
|
CHECK(at_midnight.start(peer(3), midnight_options, 1704065400000ULL) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
CHECK(at_midnight.get(peer(3), session));
|
|
CHECK(session.expires_at_millis == midnight_options.local_midnight_millis);
|
|
|
|
midnight_options.local_midnight_millis = 1704065400000ULL;
|
|
CHECK(at_midnight.start(peer(4), midnight_options, 1704065400000ULL) ==
|
|
Telemetry::ShareSessionResult::INVALID_ARGUMENT);
|
|
}
|
|
|
|
void requestsImmediateWorkAndAdvancesOnlyAfterAcceptance() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
constexpr uint64_t now = 100000;
|
|
CHECK(scheduler.start(peer(1), options(), now) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(1), session));
|
|
CHECK(!session.has_sent);
|
|
CHECK(session.last_sent_millis == 0);
|
|
|
|
Telemetry::ShareWork work{};
|
|
CHECK(poll(scheduler, now, false, work) == Telemetry::SharePollResult::NO_WORK);
|
|
CHECK(poll(scheduler, now, true, work) == Telemetry::SharePollResult::WORK);
|
|
CHECK(work.type == Telemetry::ShareWorkType::LOCATION);
|
|
CHECK(work.has_expiry);
|
|
CHECK(work.token != 0);
|
|
CHECK(poll(scheduler, now, true, work) == Telemetry::SharePollResult::NO_WORK);
|
|
|
|
CHECK(scheduler.get(peer(1), session));
|
|
CHECK(!session.has_sent);
|
|
const uint64_t token = session.pending_token;
|
|
CHECK(acknowledge(scheduler, peer(1), token, true, now + 10) ==
|
|
Telemetry::ShareAckResult::ACCEPTED);
|
|
CHECK(scheduler.get(peer(1), session));
|
|
CHECK(session.has_sent);
|
|
CHECK(session.last_sent_millis == now + 10);
|
|
CHECK(poll(scheduler, now + 10 + 59999, true, work) ==
|
|
Telemetry::SharePollResult::NO_WORK);
|
|
CHECK(poll(scheduler, now + 10 + 60000, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
}
|
|
|
|
void retriesFailuresWithBoundedBackoffWithoutExtendingExpiry() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
constexpr uint64_t start = 1000;
|
|
auto config = options();
|
|
CHECK(scheduler.start(peer(2), config, start) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
|
|
Telemetry::ShareWork work{};
|
|
CHECK(poll(scheduler, start, true, work) == Telemetry::SharePollResult::WORK);
|
|
CHECK(acknowledge(scheduler, peer(2), work.token, false, start) ==
|
|
Telemetry::ShareAckResult::RETRY_SCHEDULED);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(2), session));
|
|
CHECK(!session.has_sent);
|
|
CHECK(session.next_attempt_millis == start + Telemetry::INITIAL_RETRY_MILLIS);
|
|
const uint64_t expiry = session.expires_at_millis;
|
|
CHECK(poll(scheduler, session.next_attempt_millis - 1, true, work) ==
|
|
Telemetry::SharePollResult::NO_WORK);
|
|
CHECK(poll(scheduler, session.next_attempt_millis, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
|
|
uint64_t now = session.next_attempt_millis;
|
|
for (int failure = 0; failure < 12; ++failure) {
|
|
CHECK(acknowledge(scheduler, peer(2), work.token, false, now) ==
|
|
Telemetry::ShareAckResult::RETRY_SCHEDULED);
|
|
CHECK(scheduler.get(peer(2), session));
|
|
CHECK(session.next_attempt_millis >= now);
|
|
CHECK(session.next_attempt_millis - now <= Telemetry::MAX_RETRY_MILLIS);
|
|
CHECK(session.expires_at_millis == expiry);
|
|
now = session.next_attempt_millis;
|
|
if (now >= expiry) break;
|
|
CHECK(poll(scheduler, now, true, work) == Telemetry::SharePollResult::WORK);
|
|
}
|
|
}
|
|
|
|
void expirationQueuesExactlyOneCeaseUntilAccepted() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
constexpr uint64_t start = 1000;
|
|
CHECK(scheduler.start(peer(3), options(), start) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(3), session));
|
|
const uint64_t expiry = session.expires_at_millis;
|
|
|
|
Telemetry::ShareWork work{};
|
|
CHECK(poll(scheduler, expiry - 1, false, work) == Telemetry::SharePollResult::NO_WORK);
|
|
CHECK(poll(scheduler, expiry, false, work) == Telemetry::SharePollResult::WORK);
|
|
CHECK(work.type == Telemetry::ShareWorkType::CEASE);
|
|
const uint64_t first_token = work.token;
|
|
CHECK(poll(scheduler, expiry, false, work) == Telemetry::SharePollResult::NO_WORK);
|
|
CHECK(acknowledge(scheduler, peer(3), first_token, false, expiry) ==
|
|
Telemetry::ShareAckResult::RETRY_SCHEDULED);
|
|
CHECK(scheduler.get(peer(3), session));
|
|
CHECK(session.cease_pending);
|
|
CHECK(poll(scheduler, expiry + Telemetry::INITIAL_RETRY_MILLIS - 1,
|
|
false, work) == Telemetry::SharePollResult::NO_WORK);
|
|
CHECK(poll(scheduler, expiry + Telemetry::INITIAL_RETRY_MILLIS,
|
|
false, work) == Telemetry::SharePollResult::WORK);
|
|
CHECK(work.type == Telemetry::ShareWorkType::CEASE);
|
|
CHECK(work.token != first_token);
|
|
CHECK(acknowledge(scheduler, peer(3), work.token, true,
|
|
expiry + Telemetry::INITIAL_RETRY_MILLIS) ==
|
|
Telemetry::ShareAckResult::CEASED);
|
|
CHECK(scheduler.size() == 0);
|
|
CHECK(poll(scheduler, expiry + Telemetry::INITIAL_RETRY_MILLIS,
|
|
true, work) == Telemetry::SharePollResult::NO_WORK);
|
|
}
|
|
|
|
void unacknowledgedLocationLeaseCannotBlockExpiryForever() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
constexpr uint64_t start = 1000;
|
|
CHECK(scheduler.start(peer(14), options(), start) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(14), session));
|
|
const uint64_t expiry = session.expires_at_millis;
|
|
|
|
Telemetry::ShareWork location_work{};
|
|
CHECK(poll(scheduler, expiry - 1, true, location_work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(location_work.type == Telemetry::ShareWorkType::LOCATION);
|
|
CHECK(location_work.ack_deadline_monotonic_millis > expiry);
|
|
CHECK(poll(scheduler, expiry, false, location_work) ==
|
|
Telemetry::SharePollResult::NO_WORK);
|
|
|
|
Telemetry::ShareWork cease_work{};
|
|
CHECK(poll(scheduler, location_work.ack_deadline_monotonic_millis, false, cease_work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(cease_work.type == Telemetry::ShareWorkType::CEASE);
|
|
CHECK(acknowledge(scheduler, peer(14), location_work.token, true,
|
|
location_work.ack_deadline_monotonic_millis) ==
|
|
Telemetry::ShareAckResult::STALE_TOKEN);
|
|
CHECK(acknowledge(scheduler, peer(14), cease_work.token, true,
|
|
location_work.ack_deadline_monotonic_millis) ==
|
|
Telemetry::ShareAckResult::CEASED);
|
|
}
|
|
|
|
void repeatedStopPreservesTheSingleOutstandingCease() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
CHECK(scheduler.start(peer(15), options(), 1000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
CHECK(scheduler.stop(peer(15), 1001) ==
|
|
Telemetry::ShareSessionResult::STOPPING);
|
|
Telemetry::ShareWork work{};
|
|
CHECK(poll(scheduler, 1001, false, work) == Telemetry::SharePollResult::WORK);
|
|
CHECK(work.type == Telemetry::ShareWorkType::CEASE);
|
|
const uint64_t token = work.token;
|
|
CHECK(scheduler.stop(peer(15), 1002) ==
|
|
Telemetry::ShareSessionResult::STOPPING);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(15), session));
|
|
CHECK(session.awaiting_ack);
|
|
CHECK(session.pending_token == token);
|
|
CHECK(poll(scheduler, 1002, false, work) == Telemetry::SharePollResult::NO_WORK);
|
|
CHECK(acknowledge(scheduler, peer(15), token, true, 1003) ==
|
|
Telemetry::ShareAckResult::CEASED);
|
|
}
|
|
|
|
void serializesUpdateAndStopBehindOutstandingWork() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
constexpr uint64_t now = 10000;
|
|
CHECK(scheduler.start(peer(4), options(), now) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
Telemetry::ShareWork work{};
|
|
CHECK(poll(scheduler, now, true, work) == Telemetry::SharePollResult::WORK);
|
|
const uint64_t first_token = work.token;
|
|
|
|
auto updated = options(Telemetry::ShareDuration::HOUR_1, 30000);
|
|
CHECK(scheduler.start(peer(4), updated, now + 1) ==
|
|
Telemetry::ShareSessionResult::BUSY);
|
|
CHECK(!scheduler.cancelWithoutCease(peer(4)));
|
|
CHECK(acknowledge(scheduler, peer(4), first_token, true, now + 2) ==
|
|
Telemetry::ShareAckResult::ACCEPTED);
|
|
CHECK(scheduler.start(peer(4), updated, now + 3) ==
|
|
Telemetry::ShareSessionResult::UPDATED);
|
|
CHECK(poll(scheduler, now + 3, true, work) == Telemetry::SharePollResult::WORK);
|
|
|
|
CHECK(scheduler.stop(peer(4), now + 4) ==
|
|
Telemetry::ShareSessionResult::STOPPING);
|
|
CHECK(acknowledge(scheduler, peer(4), work.token, true, now + 5) ==
|
|
Telemetry::ShareAckResult::ACCEPTED);
|
|
CHECK(poll(scheduler, now + 5, false, work) == Telemetry::SharePollResult::WORK);
|
|
CHECK(work.type == Telemetry::ShareWorkType::CEASE);
|
|
CHECK(acknowledge(scheduler, peer(4), work.token, true, now + 6) ==
|
|
Telemetry::ShareAckResult::CEASED);
|
|
CHECK(scheduler.stop(peer(4), now + 7) ==
|
|
Telemetry::ShareSessionResult::NOT_FOUND);
|
|
}
|
|
|
|
void enforcesCapacityWithoutEvictingConsent() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
for (std::size_t index = 0; index < Telemetry::MAX_SHARE_SESSIONS; ++index) {
|
|
CHECK(scheduler.start(peer(static_cast<uint8_t>(index)), options(), 1000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
}
|
|
CHECK(scheduler.start(peer(200), options(), 1000) ==
|
|
Telemetry::ShareSessionResult::CAPACITY);
|
|
CHECK(scheduler.size() == Telemetry::MAX_SHARE_SESSIONS);
|
|
CHECK(scheduler.cancelWithoutCease(peer(10)));
|
|
CHECK(scheduler.start(peer(200), options(), 1000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
CHECK(scheduler.size() == Telemetry::MAX_SHARE_SESSIONS);
|
|
}
|
|
|
|
void restoresOnlyValidUnexpiredConsentAndRequiresGps() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
Telemetry::ShareRestoreRecord record{};
|
|
record.cadence_millis = 60000;
|
|
record.has_expiry = true;
|
|
record.expires_at_millis = 2000;
|
|
record.approx_radius_meters = 50;
|
|
|
|
CHECK(scheduler.restore(peer(5), record, 2000) ==
|
|
Telemetry::ShareSessionResult::EXPIRED);
|
|
CHECK(scheduler.size() == 0);
|
|
CHECK(scheduler.restore(peer(5), record, 1999) ==
|
|
Telemetry::ShareSessionResult::RESTORED);
|
|
Telemetry::ShareWork work{};
|
|
CHECK(poll(scheduler, 1999, false, work) == Telemetry::SharePollResult::NO_WORK);
|
|
CHECK(poll(scheduler, 1999, true, work) == Telemetry::SharePollResult::WORK);
|
|
CHECK(work.type == Telemetry::ShareWorkType::LOCATION);
|
|
CHECK(work.approx_radius_meters == 50);
|
|
}
|
|
|
|
void handlesUnavailableAndBackwardClocksFailClosed() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
CHECK(scheduler.start(peer(6), options(), 0) ==
|
|
Telemetry::ShareSessionResult::CLOCK_UNAVAILABLE);
|
|
Telemetry::ShareWork work{};
|
|
CHECK(poll(scheduler, 0, true, work) ==
|
|
Telemetry::SharePollResult::CLOCK_UNAVAILABLE);
|
|
CHECK(scheduler.size() == 0);
|
|
|
|
CHECK(scheduler.start(peer(6), options(Telemetry::ShareDuration::INDEFINITE),
|
|
100000) == Telemetry::ShareSessionResult::STARTED);
|
|
CHECK(scheduler.poll(100000, 1000, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(scheduler.acknowledge(peer(6), work.token, true, 100000, 1001) ==
|
|
Telemetry::ShareAckResult::ACCEPTED);
|
|
CHECK(scheduler.poll(90000, 1002, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(work.type == Telemetry::ShareWorkType::LOCATION);
|
|
const uint64_t invalid_clock =
|
|
static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1ULL;
|
|
CHECK(scheduler.acknowledge(
|
|
peer(6), work.token, true, invalid_clock, 1003) ==
|
|
Telemetry::ShareAckResult::CLOCK_UNAVAILABLE);
|
|
CHECK(scheduler.acknowledge(peer(6), work.token, true, 90000, 1003) ==
|
|
Telemetry::ShareAckResult::ACCEPTED);
|
|
CHECK(poll(scheduler, invalid_clock, true, work) ==
|
|
Telemetry::SharePollResult::CLOCK_UNAVAILABLE);
|
|
CHECK(scheduler.stop(peer(6), invalid_clock) ==
|
|
Telemetry::ShareSessionResult::INVALID_ARGUMENT);
|
|
}
|
|
|
|
void rebasesExistingSessionsWhenAnotherSessionObservesClockRollback() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
const auto indefinite = options(Telemetry::ShareDuration::INDEFINITE);
|
|
CHECK(scheduler.start(peer(11), indefinite, 100000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
CHECK(scheduler.start(peer(12), indefinite, 100000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
Telemetry::ShareWork work{};
|
|
CHECK(scheduler.poll(100000, 1000, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(scheduler.acknowledge(
|
|
work.peer, work.token, true, 100000, 1001) ==
|
|
Telemetry::ShareAckResult::ACCEPTED);
|
|
CHECK(scheduler.poll(100000, 1002, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(scheduler.acknowledge(
|
|
work.peer, work.token, true, 100000, 1003) ==
|
|
Telemetry::ShareAckResult::ACCEPTED);
|
|
|
|
CHECK(scheduler.start(peer(13), indefinite, 90000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
CHECK(scheduler.poll(90000, 1004, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(work.peer.bytes[0] == 11);
|
|
}
|
|
|
|
void enforcesExclusiveMonotonicLeaseBoundaries() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
CHECK(scheduler.start(peer(16),
|
|
options(Telemetry::ShareDuration::INDEFINITE), 1000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
Telemetry::ShareWork work{};
|
|
CHECK(scheduler.poll(1000, 5000, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(work.ack_deadline_monotonic_millis ==
|
|
5000 + Telemetry::ACKNOWLEDGEMENT_LEASE_MILLIS);
|
|
CHECK(scheduler.acknowledge(peer(16), work.token, true, 1000, 4999) ==
|
|
Telemetry::ShareAckResult::CLOCK_UNAVAILABLE);
|
|
CHECK(scheduler.acknowledge(peer(16), work.token, true, 1000,
|
|
work.ack_deadline_monotonic_millis) ==
|
|
Telemetry::ShareAckResult::STALE_TOKEN);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(16), session));
|
|
CHECK(!session.awaiting_ack);
|
|
CHECK(!session.has_sent);
|
|
|
|
Telemetry::LocationShareScheduler overflow;
|
|
CHECK(overflow.start(peer(17),
|
|
options(Telemetry::ShareDuration::INDEFINITE), 1000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
CHECK(overflow.poll(1000, std::numeric_limits<uint64_t>::max(), true, work) ==
|
|
Telemetry::SharePollResult::CLOCK_UNAVAILABLE);
|
|
CHECK(overflow.get(peer(17), session));
|
|
CHECK(!session.awaiting_ack);
|
|
}
|
|
|
|
void wallClockRollbackDoesNotChangeAnAdvertisedLease() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
CHECK(scheduler.start(peer(18),
|
|
options(Telemetry::ShareDuration::INDEFINITE), 100000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
Telemetry::ShareWork work{};
|
|
CHECK(scheduler.poll(100000, 5000, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(work.ack_deadline_monotonic_millis == 6000);
|
|
CHECK(scheduler.poll(90000, 5500, true, work) ==
|
|
Telemetry::SharePollResult::NO_WORK);
|
|
CHECK(scheduler.acknowledge(peer(18), work.token, true, 90000, 5999) ==
|
|
Telemetry::ShareAckResult::ACCEPTED);
|
|
}
|
|
|
|
void wallClockMaximumDoesNotOverflowTheLeaseDomain() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
const uint64_t wall_max =
|
|
static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
|
|
CHECK(scheduler.start(peer(19),
|
|
options(Telemetry::ShareDuration::INDEFINITE), wall_max) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
Telemetry::ShareWork work{};
|
|
CHECK(scheduler.poll(wall_max, 100, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(work.ack_deadline_monotonic_millis ==
|
|
100 + Telemetry::ACKNOWLEDGEMENT_LEASE_MILLIS);
|
|
CHECK(scheduler.acknowledge(peer(19), work.token, true, wall_max, 101) ==
|
|
Telemetry::ShareAckResult::ACCEPTED);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(19), session));
|
|
CHECK(session.next_attempt_millis == wall_max);
|
|
}
|
|
|
|
void rejectsInvalidConfigurationAndCorruptRestoreState() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
auto config = options();
|
|
config.cadence_millis = Telemetry::MIN_SHARE_CADENCE_MILLIS - 1;
|
|
CHECK(scheduler.start(peer(8), config, 1000) ==
|
|
Telemetry::ShareSessionResult::INVALID_ARGUMENT);
|
|
config = options();
|
|
config.cadence_millis = Telemetry::MAX_SHARE_CADENCE_MILLIS + 1U;
|
|
CHECK(scheduler.start(peer(8), config, 1000) ==
|
|
Telemetry::ShareSessionResult::INVALID_ARGUMENT);
|
|
config = options();
|
|
config.approx_radius_meters = -1;
|
|
CHECK(scheduler.start(peer(8), config, 1000) ==
|
|
Telemetry::ShareSessionResult::INVALID_ARGUMENT);
|
|
config = options();
|
|
CHECK(scheduler.start(
|
|
peer(8), config,
|
|
static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) ==
|
|
Telemetry::ShareSessionResult::INVALID_ARGUMENT);
|
|
|
|
Telemetry::ShareRestoreRecord record{};
|
|
record.cadence_millis = 60000;
|
|
record.has_expiry = false;
|
|
record.expires_at_millis = 1234;
|
|
CHECK(scheduler.restore(peer(8), record, 1000) ==
|
|
Telemetry::ShareSessionResult::INVALID_ARGUMENT);
|
|
record.expires_at_millis = 0;
|
|
record.approx_radius_meters = -1;
|
|
CHECK(scheduler.restore(peer(8), record, 1000) ==
|
|
Telemetry::ShareSessionResult::INVALID_ARGUMENT);
|
|
CHECK(scheduler.size() == 0);
|
|
}
|
|
|
|
void snapshotsOnlyDurableConsentFieldsIntoCallerStorage() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
auto first = options(Telemetry::ShareDuration::HOUR_1, 30000);
|
|
first.approx_radius_meters = 25;
|
|
CHECK(scheduler.start(peer(9), first, 1000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
CHECK(scheduler.start(peer(10), options(Telemetry::ShareDuration::INDEFINITE),
|
|
1000) == Telemetry::ShareSessionResult::STARTED);
|
|
|
|
Telemetry::ShareRestoreEntry too_small[1]{};
|
|
too_small[0].record.cadence_millis = 1234;
|
|
std::size_t written = 0;
|
|
CHECK(scheduler.snapshot(too_small, 1, written) ==
|
|
Telemetry::ShareSnapshotResult::BUFFER_TOO_SMALL);
|
|
CHECK(written == 2);
|
|
CHECK(too_small[0].record.cadence_millis == 1234);
|
|
|
|
Telemetry::ShareRestoreEntry entries[2]{};
|
|
CHECK(scheduler.snapshot(entries, 2, written) ==
|
|
Telemetry::ShareSnapshotResult::OK);
|
|
CHECK(written == 2);
|
|
CHECK(entries[0].record.cadence_millis == 30000);
|
|
CHECK(entries[0].record.approx_radius_meters == 25);
|
|
CHECK(entries[0].record.has_expiry);
|
|
const uint64_t saved_expiry = entries[0].record.expires_at_millis;
|
|
|
|
CHECK(scheduler.stop(peer(9), 1001) ==
|
|
Telemetry::ShareSessionResult::STOPPING);
|
|
CHECK(entries[0].record.expires_at_millis == saved_expiry);
|
|
CHECK(scheduler.snapshot(entries, 2, written) ==
|
|
Telemetry::ShareSnapshotResult::OK);
|
|
CHECK(written == 2);
|
|
CHECK(entries[0].record.cease_pending);
|
|
|
|
Telemetry::LocationShareScheduler restored;
|
|
CHECK(restored.restore(entries[0].peer, entries[0].record,
|
|
saved_expiry + 1) ==
|
|
Telemetry::ShareSessionResult::RESTORED);
|
|
Telemetry::ShareWork work{};
|
|
CHECK(poll(restored, saved_expiry + 1, false, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(work.type == Telemetry::ShareWorkType::CEASE);
|
|
|
|
CHECK(scheduler.snapshot(nullptr, 2, written) ==
|
|
Telemetry::ShareSnapshotResult::INVALID_ARGUMENT);
|
|
}
|
|
|
|
void survivesDeterministicHundredThousandOperationStress() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
CHECK(sizeof(scheduler) <= 8192);
|
|
uint32_t random = 0x9e3779b9U;
|
|
Telemetry::ShareWork work{};
|
|
for (uint64_t operation = 1; operation <= 100000; ++operation) {
|
|
random = random * 1664525U + 1013904223U;
|
|
const auto id = peer(static_cast<uint8_t>(random >> 24U));
|
|
switch (random & 0x03U) {
|
|
case 0:
|
|
scheduler.start(id, options(Telemetry::ShareDuration::INDEFINITE,
|
|
Telemetry::MIN_SHARE_CADENCE_MILLIS),
|
|
operation);
|
|
break;
|
|
case 1:
|
|
scheduler.stop(id, operation);
|
|
break;
|
|
case 2:
|
|
scheduler.cancelWithoutCease(id);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (poll(scheduler, operation, true, work) ==
|
|
Telemetry::SharePollResult::WORK) {
|
|
acknowledge(scheduler, work.peer, work.token, (random & 0x10U) != 0,
|
|
operation);
|
|
}
|
|
CHECK(scheduler.size() <= Telemetry::MAX_SHARE_SESSIONS);
|
|
}
|
|
}
|
|
|
|
void expirationPreemptsAQueuedRetry() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
constexpr uint64_t start = 1000;
|
|
CHECK(scheduler.start(peer(7), options(), start) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(7), session));
|
|
const uint64_t expiry = session.expires_at_millis;
|
|
|
|
Telemetry::ShareWork work{};
|
|
CHECK(poll(scheduler, expiry - 1000, true, work) ==
|
|
Telemetry::SharePollResult::WORK);
|
|
CHECK(acknowledge(scheduler, peer(7), work.token, false, expiry - 1000) ==
|
|
Telemetry::ShareAckResult::RETRY_SCHEDULED);
|
|
CHECK(poll(scheduler, expiry, false, work) == Telemetry::SharePollResult::WORK);
|
|
CHECK(work.type == Telemetry::ShareWorkType::CEASE);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
defaultsToNoSharing();
|
|
revisionsTrackOnlySerializedSessionMutations();
|
|
computesDurationAndMidnightBoundaries();
|
|
requestsImmediateWorkAndAdvancesOnlyAfterAcceptance();
|
|
retriesFailuresWithBoundedBackoffWithoutExtendingExpiry();
|
|
expirationQueuesExactlyOneCeaseUntilAccepted();
|
|
unacknowledgedLocationLeaseCannotBlockExpiryForever();
|
|
repeatedStopPreservesTheSingleOutstandingCease();
|
|
serializesUpdateAndStopBehindOutstandingWork();
|
|
enforcesCapacityWithoutEvictingConsent();
|
|
restoresOnlyValidUnexpiredConsentAndRequiresGps();
|
|
handlesUnavailableAndBackwardClocksFailClosed();
|
|
rebasesExistingSessionsWhenAnotherSessionObservesClockRollback();
|
|
enforcesExclusiveMonotonicLeaseBoundaries();
|
|
wallClockRollbackDoesNotChangeAnAdvertisedLease();
|
|
wallClockMaximumDoesNotOverflowTheLeaseDomain();
|
|
rejectsInvalidConfigurationAndCorruptRestoreState();
|
|
snapshotsOnlyDurableConsentFieldsIntoCallerStorage();
|
|
expirationPreemptsAQueuedRetry();
|
|
survivesDeterministicHundredThousandOperationStress();
|
|
std::cout << "location share scheduler: " << passed << " passed, "
|
|
<< failures << " failed\n";
|
|
return failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|