wifi-densepose/v2/crates/wifi-densepose-bfld/tests/example_handle.rs

121 lines
4.0 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! Validate `examples/bfld_handle.rs` operator quickstart. Re-runs the same
//! lifecycle inline so CI proves the worker-thread pattern works end-to-end.
#![cfg(feature = "std")]
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use wifi_densepose_bfld::{
publish_availability_offline, publish_availability_online, publish_discovery, BfldConfig,
BfldPipeline, BfldPipelineHandle, CapturePublisher, IdentityEmbedding, PipelineInput,
PrivacyClass, SensingInputs, SignatureHasher, EMBEDDING_DIM, SITE_SALT_LEN,
};
const HANDLE_EXAMPLE: &str = include_str!("../examples/bfld_handle.rs");
#[test]
fn handle_example_documents_full_lifecycle_phases() {
// Doc drift guard: every operator-facing symbol must appear in the file.
for needle in [
"publish_availability_online",
"publish_discovery",
"BfldPipelineHandle::spawn",
"handle.send",
"handle.shutdown",
"publish_availability_offline",
"SignatureHasher",
"PipelineInput",
] {
assert!(
HANDLE_EXAMPLE.contains(needle),
"example must reference {needle}",
);
}
}
#[test]
fn handle_example_carries_run_instructions_and_prod_pointer() {
assert!(
HANDLE_EXAMPLE.contains("cargo run -p wifi-densepose-bfld --example bfld_handle"),
"example must document its own run command",
);
assert!(
HANDLE_EXAMPLE.contains("RumqttPublisher::connect_with_lwt"),
"example must point operators at the production publisher path",
);
}
#[test]
fn handle_example_lifecycle_produces_expected_message_counts() {
// Re-execute the lifecycle inline. End state must show:
// 1 (online) + 6 (discovery anonymous + zone-less) + 5×5 (state per
// send) + 1 (offline) = 33 messages.
let node_id = "seed-handle-test";
let site_salt: [u8; SITE_SALT_LEN] = [0xC0; SITE_SALT_LEN];
let publisher = Arc::new(Mutex::new(CapturePublisher::default()));
publish_availability_online(&mut publisher.clone(), node_id).expect("online");
let discovery_count =
publish_discovery(&mut publisher.clone(), node_id, PrivacyClass::Anonymous)
.expect("discovery");
assert_eq!(discovery_count, 6);
let pipeline = BfldPipeline::new(
BfldConfig::new(node_id).with_signature_hasher(SignatureHasher::new(site_salt)),
);
let handle = BfldPipelineHandle::spawn(pipeline, publisher.clone());
for i in 0..5u64 {
let timestamp_ns = 1_700_000_000_000_000_000 + i * 200_000_000;
let input = PipelineInput {
inputs: SensingInputs {
timestamp_ns,
presence: true,
motion: 0.3 + (i as f32) * 0.1,
person_count: 1,
sensing_confidence: 0.9,
sep: 0.2,
stab: 0.2,
consist: 0.2,
risk_conf: 0.2,
rf_signature_hash: None,
},
embedding: Some(IdentityEmbedding::from_raw([0.05; EMBEDDING_DIM])),
};
handle.send(input).expect("send");
}
thread::sleep(Duration::from_millis(120));
handle.shutdown();
publish_availability_offline(&mut publisher.clone(), node_id).expect("offline");
let log = publisher.lock().expect("publisher mutex");
let total = log.published.len();
// Expected: 1 online + 6 discovery + 5 × 5 state + 1 offline = 33.
assert_eq!(
total, 33,
"expected 33 total messages from full lifecycle, got {total}; \
topics: {:?}",
log.published
.iter()
.map(|m| &m.topic)
.collect::<Vec<_>>(),
);
// First message is the online availability.
assert_eq!(log.published[0].payload, "online");
// Last message is the offline availability.
assert_eq!(log.published[total - 1].payload, "offline");
}
#[test]
fn handle_example_returns_box_dyn_error_for_main_signature() {
assert!(
HANDLE_EXAMPLE.contains("fn main() -> Result<(), Box<dyn std::error::Error>>"),
);
}