macos-collector/Tests/CollectorCoreTests/WireTests.swift
66 lines · 3167 bytes
1import XCTest
2@testable import CollectorCore
3
4final class WireTests: XCTestCase {
5 /// The canonical hardware frame, shared **verbatim** with the Rust decode
6 /// test (`crates/signal-schema/tests/hardware_wire.rs`). Both languages
7 /// independently commit to these exact bytes — that shared literal *is* the
8 /// cross-language wire contract, so no cross-process execution is needed to
9 /// prove Swift and Rust agree.
10 ///
11 /// Signal: schema_version=3, ts=0, source=Hardware(3), name=CpuLoad(28),
12 /// value=0.5, tag=none.
13 func testCanonicalHardwareFrameMatchesRust() {
14 let signal = Signal(ts: 0, source: .hardware, name: .cpuLoad, value: 0.5)
15 let expected: [UInt8] = [
16 0x15, 0x00, 0x00, 0x00, // body_len = 21
17 0x03, 0x00, // schema_version = 3
18 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ts = 0
19 0x03, // source = Hardware
20 0x1C, // name = CpuLoad (28)
21 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x3F, // value = 0.5 (f64 LE)
22 0x00, // tag_present = 0
23 ]
24 XCTAssertEqual(encode(signal), expected)
25 }
26
27 /// The schema version must equal the Rust `SCHEMA_VERSION`, or the daemon
28 /// drops every frame this collector sends.
29 func testSchemaVersionMatchesRust() {
30 XCTAssertEqual(SCHEMA_VERSION, 3)
31 }
32
33 /// The tagged-frame layout matches Rust too (hardware signals are untagged,
34 /// but the encoder must still agree on the audited-tag case).
35 func testTaggedFrameLayout() {
36 let signal = Signal(ts: 0, source: .macos, name: .batteryPct, value: 1.0, tag: "ab")
37 let frame = encode(signal)
38 // body = 2+8+1+1+8+1 + (2 + 2) = 25 ; frame = 4 + 25 = 29
39 XCTAssertEqual(frame.count, 29)
40 XCTAssertEqual(Array(frame.prefix(4)), [0x19, 0x00, 0x00, 0x00]) // body_len = 25
41 XCTAssertEqual(frame[24], 0x01) // tag_present
42 XCTAssertEqual([frame[25], frame[26]], [0x02, 0x00]) // tag_len = 2
43 XCTAssertEqual([frame[27], frame[28]], Array("ab".utf8))
44 }
45
46 /// Hardware signals never carry a tag and never carry content — the payload
47 /// is a single `Double`. This mirrors the Rust privacy invariant.
48 func testHardwareSignalsAreUntaggedScalars() {
49 for name in [SignalName.cpuLoad, .batteryPct, .charging, .batteryDrawW, .thermalState] {
50 let s = Signal(ts: 1, source: .hardware, name: name, value: 0.0)
51 XCTAssertNil(s.tag)
52 }
53 }
54
55 /// The IOKit CPU read yields a plausible fraction in `[0, 1]` (or nil while
56 /// unavailable) — a real read against real hardware.
57 func testCPUSamplerInRange() {
58 let sampler = CPUSampler()
59 _ = sampler.sample()
60 Thread.sleep(forTimeInterval: 0.1)
61 if let load = sampler.sample() {
62 XCTAssertGreaterThanOrEqual(load, 0.0)
63 XCTAssertLessThanOrEqual(load, 1.0)
64 }
65 }
66}