mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-08-20 17:49:51 +00:00
200 lines
9.1 KiB
C++
200 lines
9.1 KiB
C++
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <iostream>
|
|
|
|
#include "Telemetry/LocationLxmfAdapter.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 value{};
|
|
for (std::size_t i = 0; i < Telemetry::PEER_ID_SIZE; ++i) value.bytes[i] = static_cast<uint8_t>(seed + i);
|
|
return value;
|
|
}
|
|
|
|
Telemetry::LocationTelemetry location(uint64_t seconds) {
|
|
Telemetry::LocationTelemetry value{};
|
|
value.latitude_e6 = 37774900;
|
|
value.longitude_e6 = -122419400;
|
|
value.altitude_cm = 1600;
|
|
value.speed_centi_kmh = 1234;
|
|
value.bearing_cdeg = 4200;
|
|
value.accuracy_cm = 350;
|
|
value.timestamp_seconds = seconds;
|
|
value.sensor_timestamp_seconds = seconds;
|
|
return value;
|
|
}
|
|
|
|
class FakeRouter : public Telemetry::LocationEnvelopeRouter {
|
|
public:
|
|
bool accepted = true;
|
|
uint64_t ownership_time = 0;
|
|
int calls = 0;
|
|
Telemetry::OutboundLocationEnvelope envelope{};
|
|
bool queue(const Telemetry::OutboundLocationEnvelope& value,
|
|
uint64_t deadline,
|
|
uint64_t& ownership) override {
|
|
++calls;
|
|
envelope = value;
|
|
ownership = ownership_time == 0
|
|
? deadline - Telemetry::ACKNOWLEDGEMENT_LEASE_MILLIS
|
|
: ownership_time;
|
|
if (ownership >= deadline) return false;
|
|
return accepted;
|
|
}
|
|
};
|
|
|
|
void exactLocationEnvelope() {
|
|
Telemetry::ShareWork work{};
|
|
work.peer = peer(7);
|
|
work.type = Telemetry::ShareWorkType::LOCATION;
|
|
work.has_expiry = true;
|
|
work.expires_at_millis = 1700000900000ULL;
|
|
work.approx_radius_meters = 25;
|
|
Telemetry::OutboundLocationEnvelope envelope{};
|
|
CHECK(Telemetry::buildOutboundLocationEnvelope(work, location(1700000000ULL), 1700000000123ULL, envelope) == Telemetry::EnvelopeBuildResult::OK);
|
|
CHECK(std::memcmp(envelope.destination.bytes, work.peer.bytes, Telemetry::PEER_ID_SIZE) == 0);
|
|
CHECK(envelope.method == Telemetry::LocationDeliveryMethod::OPPORTUNISTIC);
|
|
CHECK(envelope.title_size == 0 && envelope.content_size == 0);
|
|
CHECK(envelope.field_count == 2);
|
|
CHECK(envelope.fields[0].key_size == 1 && envelope.fields[0].key[0] == 0x02);
|
|
CHECK(envelope.fields[1].key_size == 2 && envelope.fields[1].key[0] == 0xcc && envelope.fields[1].key[1] == 0xfd);
|
|
Telemetry::BinaryView inner{};
|
|
CHECK(Telemetry::unwrapLxmfBinaryFieldValue(envelope.fields[0].value, envelope.fields[0].value_size, inner) == Telemetry::FieldValueResult::OK);
|
|
Telemetry::LocationTelemetry decoded{};
|
|
CHECK(Telemetry::decodeLocationTelemetry(inner.data, inner.size, decoded) == Telemetry::DecodeResult::OK);
|
|
CHECK(decoded.latitude_e6 == 37774900 && decoded.timestamp_seconds == 1700000000ULL);
|
|
Telemetry::BinaryView encoded_meta{};
|
|
CHECK(Telemetry::unwrapLxmfBinaryFieldValue(
|
|
envelope.fields[1].value, envelope.fields[1].value_size,
|
|
encoded_meta) == Telemetry::FieldValueResult::OK);
|
|
Telemetry::CustomLocationMeta meta{};
|
|
CHECK(Telemetry::decodeCustomLocationMeta(
|
|
encoded_meta.data, encoded_meta.size, meta) ==
|
|
Telemetry::CustomMetaResult::OK);
|
|
CHECK(!meta.has_cease && meta.has_expires && meta.expires_millis == 1700000900000LL);
|
|
CHECK(meta.has_approx_radius && meta.approx_radius_meters == 25);
|
|
CHECK(!meta.has_timestamp);
|
|
}
|
|
|
|
void acceptanceAndRetryAreHonest() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
Telemetry::ShareStartOptions options{};
|
|
options.duration = Telemetry::ShareDuration::INDEFINITE;
|
|
CHECK(scheduler.start(peer(1), options, 100000) == Telemetry::ShareSessionResult::STARTED);
|
|
FakeRouter router;
|
|
router.accepted = false;
|
|
Telemetry::DispatchResult result = Telemetry::dispatchLocationShare(scheduler, 100000, 5000, true, location(100), router);
|
|
CHECK(result == Telemetry::DispatchResult::QUEUE_REJECTED);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(1), session) && !session.has_sent && !session.awaiting_ack);
|
|
CHECK(session.next_attempt_millis == 100000 + Telemetry::INITIAL_RETRY_MILLIS);
|
|
CHECK(Telemetry::dispatchLocationShare(scheduler, session.next_attempt_millis - 1, 5001, true, location(100), router) == Telemetry::DispatchResult::NO_WORK);
|
|
router.accepted = true;
|
|
CHECK(Telemetry::dispatchLocationShare(scheduler, session.next_attempt_millis, 5002, true, location(105), router) == Telemetry::DispatchResult::QUEUED);
|
|
CHECK(scheduler.get(peer(1), session) && session.has_sent && !session.awaiting_ack);
|
|
}
|
|
|
|
void ceaseShapeAndOwnership() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
Telemetry::ShareStartOptions options{};
|
|
options.duration = Telemetry::ShareDuration::INDEFINITE;
|
|
CHECK(scheduler.start(peer(3), options, 1000) == Telemetry::ShareSessionResult::STARTED);
|
|
FakeRouter router;
|
|
CHECK(Telemetry::dispatchLocationShare(scheduler, 1000, 1000, true, location(1), router) == Telemetry::DispatchResult::QUEUED);
|
|
CHECK(scheduler.stop(peer(3), 1001) == Telemetry::ShareSessionResult::STOPPING);
|
|
router.accepted = false;
|
|
CHECK(Telemetry::dispatchLocationShare(scheduler, 1001, 1001, false, location(1), router) == Telemetry::DispatchResult::QUEUE_REJECTED);
|
|
Telemetry::CustomLocationMeta meta{};
|
|
Telemetry::BinaryView encoded_meta{};
|
|
CHECK(Telemetry::unwrapLxmfBinaryFieldValue(
|
|
router.envelope.fields[1].value,
|
|
router.envelope.fields[1].value_size,
|
|
encoded_meta) == Telemetry::FieldValueResult::OK);
|
|
CHECK(Telemetry::decodeCustomLocationMeta(
|
|
encoded_meta.data, encoded_meta.size, meta) ==
|
|
Telemetry::CustomMetaResult::OK);
|
|
CHECK(meta.has_cease && meta.cease);
|
|
Telemetry::BinaryView inner{};
|
|
CHECK(Telemetry::unwrapLxmfBinaryFieldValue(router.envelope.fields[0].value, router.envelope.fields[0].value_size, inner) == Telemetry::FieldValueResult::OK);
|
|
Telemetry::LocationTelemetry zero{};
|
|
CHECK(Telemetry::decodeLocationTelemetry(inner.data, inner.size, zero) == Telemetry::DecodeResult::OK);
|
|
CHECK(zero.latitude_e6 == 0 && zero.longitude_e6 == 0);
|
|
CHECK(zero.timestamp_seconds == 1U &&
|
|
zero.sensor_timestamp_seconds == 1U);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(3), session));
|
|
router.accepted = true;
|
|
CHECK(Telemetry::dispatchLocationShare(scheduler, session.next_attempt_millis, 2000, false, location(1), router) == Telemetry::DispatchResult::CEASE_QUEUED);
|
|
CHECK(scheduler.size() == 0);
|
|
}
|
|
|
|
void optionalMetadataIsOmitted() {
|
|
Telemetry::ShareWork work{};
|
|
work.peer = peer(8);
|
|
Telemetry::OutboundLocationEnvelope envelope{};
|
|
CHECK(Telemetry::buildOutboundLocationEnvelope(
|
|
work, location(1), 1000, envelope) ==
|
|
Telemetry::EnvelopeBuildResult::OK);
|
|
CHECK(envelope.field_count == 1);
|
|
|
|
work.has_approx_radius = true;
|
|
CHECK(Telemetry::buildOutboundLocationEnvelope(
|
|
work, location(1), 1000, envelope) ==
|
|
Telemetry::EnvelopeBuildResult::OK);
|
|
CHECK(envelope.field_count == 2);
|
|
Telemetry::BinaryView encoded_meta{};
|
|
CHECK(Telemetry::unwrapLxmfBinaryFieldValue(
|
|
envelope.fields[1].value, envelope.fields[1].value_size,
|
|
encoded_meta) == Telemetry::FieldValueResult::OK);
|
|
Telemetry::CustomLocationMeta meta{};
|
|
CHECK(Telemetry::decodeCustomLocationMeta(
|
|
encoded_meta.data, encoded_meta.size, meta) ==
|
|
Telemetry::CustomMetaResult::OK);
|
|
CHECK(meta.has_approx_radius && meta.approx_radius_meters == 0);
|
|
}
|
|
|
|
void expiredOwnershipLeaseIsRejected() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
Telemetry::ShareStartOptions options{};
|
|
options.duration = Telemetry::ShareDuration::INDEFINITE;
|
|
CHECK(scheduler.start(peer(12), options, 1000) ==
|
|
Telemetry::ShareSessionResult::STARTED);
|
|
FakeRouter router;
|
|
router.ownership_time = 2000;
|
|
CHECK(Telemetry::dispatchLocationShare(
|
|
scheduler, 1000, 1000, true, location(1), router) ==
|
|
Telemetry::DispatchResult::QUEUE_REJECTED);
|
|
Telemetry::ShareSession session{};
|
|
CHECK(scheduler.get(peer(12), session) && !session.has_sent &&
|
|
!session.awaiting_ack);
|
|
}
|
|
|
|
void noSideEffectsOrWorkWithoutValidGps() {
|
|
Telemetry::LocationShareScheduler scheduler;
|
|
Telemetry::ShareStartOptions options{};
|
|
options.duration = Telemetry::ShareDuration::INDEFINITE;
|
|
CHECK(scheduler.start(peer(4), options, 1000) == Telemetry::ShareSessionResult::STARTED);
|
|
FakeRouter router;
|
|
CHECK(Telemetry::dispatchLocationShare(scheduler, 1000, 1000, false, location(1), router) == Telemetry::DispatchResult::NO_WORK);
|
|
CHECK(router.calls == 0);
|
|
CHECK(sizeof(Telemetry::OutboundLocationEnvelope) <= 768U);
|
|
}
|
|
}
|
|
|
|
int main() {
|
|
exactLocationEnvelope();
|
|
acceptanceAndRetryAreHonest();
|
|
ceaseShapeAndOwnership();
|
|
optionalMetadataIsOmitted();
|
|
expiredOwnershipLeaseIsRejected();
|
|
noSideEffectsOrWorkWithoutValidGps();
|
|
std::cout << "location LXMF adapter: " << passed << " passed, " << failures << " failed\n";
|
|
return failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|