test(core): probe the agreed 1.0 public surface
public_api.rs imported only spec §3's original list (S15). It now also probes WallClockStore, Node::set_wall_clock, Node::peer_time_estimate and Node::flush (decision H), the Node method signatures, the NodeEvent shape and the public wire constants, and names nothing §3 makes private (Frame, WireError, local constants). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,21 +1,26 @@
|
|||||||
//! Compile probe for the tines-core 1.0 public surface (Task 5).
|
//! Compile probe for the tines-core 1.0 public surface (spec §3, decisions H
|
||||||
|
//! and K).
|
||||||
//!
|
//!
|
||||||
//! Every `use` path here must keep resolving against `tines_core`. A future
|
//! Every path and signature here must keep resolving against `tines_core`. A
|
||||||
//! change that removes any of these items from the public API — or drops a
|
//! change that removes or reshapes any of these items fails to build here.
|
||||||
//! crate-root re-export that Task 5 established — fails to build here, which
|
//! CI compiles it with and without the default (`alloc`) feature.
|
||||||
//! is exactly what the probe guards against. CI compiles it on the workspace
|
//!
|
||||||
//! both with and without the default (alloc) features.
|
//! The probe deliberately names nothing that §3 makes private: `Frame`,
|
||||||
#![allow(unused_imports)]
|
//! `WireError` and the local constants (`MAX_PEERS`, `PEER_IDLE_MS`, ...).
|
||||||
|
//! Their absence is pinned by `compile_fail` doctests on `wire::Frame` and
|
||||||
|
//! `consts::MAX_PEERS` inside the crate.
|
||||||
|
#![allow(unused_imports, dead_code)]
|
||||||
|
|
||||||
// Re-exported at the crate root by Task 5's `tines_core::lib`.
|
use tines_core::consts::{
|
||||||
use tines_core::consts::WIRE_VERSION;
|
ACK_IDS_CAP, BLOOM_K, MAX_FRAME, MAX_PAYLOAD, MAX_SIG, MAX_TOPIC, SUMMARY_BYTES, WIRE_VERSION,
|
||||||
|
};
|
||||||
use tines_core::{
|
use tines_core::{
|
||||||
belief::{BeliefPolicy, LatestWins, SelfFirst},
|
belief::{BeliefPolicy, LatestWins, SelfFirst},
|
||||||
node::{Node, ObserveError},
|
node::{Node, ObserveError},
|
||||||
observation::{
|
observation::{
|
||||||
Dest, NodeId, Observation, ObservationId, Payload, SigAlgo, Signature, Tick, Topic,
|
Dest, NodeId, Observation, ObservationId, Payload, SigAlgo, Signature, Tick, Topic,
|
||||||
},
|
},
|
||||||
store::{Store, StoreError, TrimPolicy},
|
store::{Store, StoreError, TrimPolicy, WallClockStore},
|
||||||
transport::{Event, FrameBuf, SendError, Transport},
|
transport::{Event, FrameBuf, SendError, Transport},
|
||||||
DropReason, NodeEvent,
|
DropReason, NodeEvent,
|
||||||
};
|
};
|
||||||
@@ -24,3 +29,72 @@ use tines_core::{consts, frame_sender};
|
|||||||
// `MemStore` lives behind the `alloc` feature.
|
// `MemStore` lives behind the `alloc` feature.
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use tines_core::MemStore;
|
use tines_core::MemStore;
|
||||||
|
|
||||||
|
// The crate-root re-exports name the same items as their modules.
|
||||||
|
use tines_core::{
|
||||||
|
BeliefPolicy as _, Dest as _, Event as _, LatestWins as _, Node as _, Observation as _,
|
||||||
|
SelfFirst as _, SendError as _, Signature as _, Store as _, StoreError as _, Transport as _,
|
||||||
|
TrimPolicy as _, WallClockStore as _,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// `frame_sender` keeps its signature.
|
||||||
|
const _FRAME_SENDER: fn(&[u8]) -> Option<NodeId> = frame_sender;
|
||||||
|
|
||||||
|
/// The `Node` methods §3 lists, with their signatures, for any store and
|
||||||
|
/// transport.
|
||||||
|
fn node_methods<S: Store, T: Transport>(mut node: Node<S, T>, now: Tick) {
|
||||||
|
let _: NodeId = node.id();
|
||||||
|
let _: u64 = node.next_seq();
|
||||||
|
let _: Result<ObservationId, ObserveError> = node.observe(b"t", b"p", None, now);
|
||||||
|
let _: Result<ObservationId, ObserveError> =
|
||||||
|
node.observe_to([0; 16], 1_000, b"t", b"p", None, now);
|
||||||
|
let _: usize = node.poll(now);
|
||||||
|
for _ev in node.drain_events() {}
|
||||||
|
let _: Option<Observation> = node.belief(b"t", &LatestWins, now);
|
||||||
|
let _: usize = node.trim(&TrimPolicy::default(), now);
|
||||||
|
let _: &S = node.store();
|
||||||
|
let _: &mut T = node.transport_mut();
|
||||||
|
let _: Result<(), StoreError> = node.flush(now);
|
||||||
|
let _ = Node::<S, T>::new;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `set_wall_clock` and `peer_time_estimate` exist when the store is a
|
||||||
|
/// `WallClockStore` (decision H).
|
||||||
|
fn node_wall_clock<S: WallClockStore, T: Transport>(mut node: Node<S, T>, now: Tick) {
|
||||||
|
let _: S::FoldReport = node.set_wall_clock(1_700_000_000_000, now);
|
||||||
|
let _: Option<u64> = node.peer_time_estimate(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `WallClockStore`'s shape.
|
||||||
|
fn wall_clock_store<S: WallClockStore>(mut s: S, now: Tick) {
|
||||||
|
let _: S::FoldReport = s.set_wall_clock(1_700_000_000_000, now);
|
||||||
|
let _: Option<u64> = s.peer_time_estimate(now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `NodeEvent` carries the transport address and is matched with a wildcard
|
||||||
|
/// (it is `#[non_exhaustive]`).
|
||||||
|
fn node_event<A: Clone>(ev: NodeEvent<A>) {
|
||||||
|
match ev {
|
||||||
|
NodeEvent::Delivered { peer, id } => {
|
||||||
|
let _: (NodeId, ObservationId) = (peer, id);
|
||||||
|
}
|
||||||
|
NodeEvent::Dropped {
|
||||||
|
addr,
|
||||||
|
reason,
|
||||||
|
count,
|
||||||
|
} => {
|
||||||
|
let _: (Option<A>, DropReason, u32) = (addr, reason, count);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wire_constants_are_public() {
|
||||||
|
assert_eq!(WIRE_VERSION, 4);
|
||||||
|
assert_eq!(
|
||||||
|
(MAX_TOPIC, MAX_PAYLOAD, MAX_FRAME, SUMMARY_BYTES),
|
||||||
|
(64, 1024, 1500, 512)
|
||||||
|
);
|
||||||
|
assert_eq!((ACK_IDS_CAP, MAX_SIG, BLOOM_K), (90, 64, 3));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user