Ambient system companions over one privacy-preserving signal daemon (aggregate-only, no keystroke content): a git-driven terminal garden and IOKit hardware collectors. ambient daemon macos privacy terminal

crates/signal-schema/tests/hardware_wire.rs

85 lines · 3741 bytes

 1//! # The Swift↔Rust hardware wire contract (v4)
 2//!
 3//! The macOS IOKit collector is a sibling Swift package (`macos-collector/`)
 4//! that speaks this crate's wire format. Swift and Rust are two independent
 5//! implementations of the same byte layout, so the contract needs a test that
 6//! pins the *exact bytes* both sides must agree on — not just a Rust round trip.
 7//!
 8//! This test hand-encodes, per the documented frame layout
 9//! (`signal_schema::wire`), the canonical hardware frame the Swift collector
10//! emits, and asserts:
11//!   1. `wire::decode` parses those exact bytes into the expected [`Signal`]
12//!      (this is signald's parse path — signald decodes hardware frames with the
13//!      same function), and
14//!   2. `wire::encode` reproduces those exact bytes from the [`Signal`].
15//!
16//! The identical byte array is asserted on the Swift side in
17//! `macos-collector/Tests/CollectorCoreTests/WireTests.swift`, so both languages
18//! independently commit to the same wire. Cross-*process* execution of the Swift
19//! binary is not required to prove agreement — the shared literal is the proof.
20//!
21//! Privacy note: a hardware frame carries the same `f64`-only payload as every
22//! other signal. `CpuLoad` is untagged (`tag_present == 0`); there is no content
23//! field. The privacy invariant is intact for hardware signals by construction.
24
25use signal_schema::{wire, Signal, SignalName, Source, Value, SCHEMA_VERSION};
26
27/// The canonical hardware test vector, shared verbatim with the Swift side.
28///
29/// Signal: schema_version=4, ts=0, source=Hardware(3), name=CpuLoad(10),
30/// value=0.5 (`f64`), tag=None.
31///
32/// Frame bytes (little-endian throughout):
33/// ```text
34///   15 00 00 00                          body_len = 21 (u32)
35///   04 00                                schema_version = 4 (u16)
36///   00 00 00 00 00 00 00 00              ts = 0 (u64)
37///   03                                   source = Hardware (u8)
38///   0A                                   name = CpuLoad = 10 (u8)
39///   00 00 00 00 00 00 E0 3F              value = 0.5 (f64)
40///   00                                   tag_present = 0 (u8)
41/// ```
42const CANONICAL_FRAME: [u8; 25] = [
43    0x15, 0x00, 0x00, 0x00, // body_len = 21
44    0x04, 0x00, // schema_version = 4
45    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ts = 0
46    0x03, // source = Hardware
47    0x0A, // name = CpuLoad (10)
48    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x3F, // value = 0.5
49    0x00, // tag_present = 0
50];
51
52fn canonical_signal() -> Signal {
53    Signal {
54        schema_version: SCHEMA_VERSION,
55        ts: 0,
56        source: Source::Hardware,
57        name: SignalName::CpuLoad,
58        value: Value(0.5),
59        tag: None,
60    }
61}
62
63/// signald parses a hardware frame produced to the documented byte layout.
64#[test]
65fn signald_decodes_the_swift_hardware_frame() {
66    let decoded = wire::decode(&CANONICAL_FRAME).expect("signald must decode the hardware frame");
67    assert_eq!(decoded, canonical_signal());
68    // The privacy boundary holds for hardware signals too: f64 payload, no tag.
69    assert_eq!(decoded.value, Value(0.5));
70    assert!(decoded.tag.is_none(), "aggregate hardware scalar carries no tag");
71}
72
73/// The Rust encoder reproduces the exact bytes the Swift encoder commits to.
74#[test]
75fn rust_encoder_matches_the_shared_byte_vector() {
76    assert_eq!(wire::encode(&canonical_signal()), CANONICAL_FRAME.to_vec());
77}
78
79/// The aggregate CPU metric is untagged and frozen at discriminant 10.
80#[test]
81fn cpu_load_is_untagged_and_stable() {
82    assert_eq!(SignalName::CpuLoad.to_u8(), 10);
83    assert_eq!(SignalName::from_u8(10), Some(SignalName::CpuLoad));
84    assert!(!SignalName::CpuLoad.allows_tag(), "aggregate CPU load is never tagged");
85}