use std::sync::OnceLock;
use std::sync::atomic::{AtomicU64, Ordering};
use serde::Serialize;
#[derive(Debug, Default)]
pub struct GsoDiagnostics {
bundle_send_total: AtomicU64,
bundle_partial_send: AtomicU64,
}
impl GsoDiagnostics {
pub fn record_bundle_submitted(&self, segment_count: usize) {
if segment_count <= 1 {
return;
}
self.bundle_send_total.fetch_add(1, Ordering::Relaxed);
}
pub fn record_bundle_partial_send(&self) {
self.bundle_partial_send.fetch_add(1, Ordering::Relaxed);
}
pub fn bundle_send_total(&self) -> u64 {
self.bundle_send_total.load(Ordering::Relaxed)
}
pub fn bundle_partial_send(&self) -> u64 {
self.bundle_partial_send.load(Ordering::Relaxed)
}
pub fn snapshot(&self) -> GsoDiagnosticsSnapshot {
GsoDiagnosticsSnapshot {
bundle_send_total: self.bundle_send_total(),
bundle_partial_send: self.bundle_partial_send(),
}
}
}
pub fn gso_diagnostics() -> &'static GsoDiagnostics {
static GSO: OnceLock<GsoDiagnostics> = OnceLock::new();
GSO.get_or_init(GsoDiagnostics::default)
}
#[derive(Debug, Clone, Copy, Serialize)]
pub struct GsoDiagnosticsSnapshot {
pub bundle_send_total: u64,
pub bundle_partial_send: u64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn single_segment_send_does_not_increment_bundle_total() {
let diags = GsoDiagnostics::default();
diags.record_bundle_submitted(1);
diags.record_bundle_submitted(0);
let snap = diags.snapshot();
assert_eq!(snap.bundle_send_total, 0);
assert_eq!(snap.bundle_partial_send, 0);
}
#[test]
fn multi_segment_bundle_increments_bundle_send_total() {
let diags = GsoDiagnostics::default();
diags.record_bundle_submitted(2);
assert_eq!(diags.snapshot().bundle_send_total, 1);
diags.record_bundle_submitted(10);
assert_eq!(diags.snapshot().bundle_send_total, 2);
}
#[test]
fn partial_send_increments_independently() {
let diags = GsoDiagnostics::default();
diags.record_bundle_submitted(5);
diags.record_bundle_partial_send();
let snap = diags.snapshot();
assert_eq!(snap.bundle_send_total, 1);
assert_eq!(snap.bundle_partial_send, 1);
}
#[test]
fn process_global_accessor_is_idempotent() {
let a = gso_diagnostics() as *const _;
let b = gso_diagnostics() as *const _;
assert_eq!(a, b, "OnceLock must yield the same address on every call");
}
#[test]
fn submitted_counter_counts_bundles_not_segments() {
let diags = GsoDiagnostics::default();
diags.record_bundle_submitted(2);
diags.record_bundle_submitted(64);
assert_eq!(diags.bundle_send_total(), 2);
assert_eq!(diags.bundle_partial_send(), 0);
}
#[test]
fn partial_send_can_be_recorded_without_submission() {
let diags = GsoDiagnostics::default();
diags.record_bundle_partial_send();
diags.record_bundle_partial_send();
assert_eq!(diags.bundle_send_total(), 0);
assert_eq!(diags.bundle_partial_send(), 2);
}
#[test]
fn snapshot_is_copy_clone_and_debuggable() {
let snapshot = GsoDiagnosticsSnapshot {
bundle_send_total: 3,
bundle_partial_send: 1,
};
let copied = snapshot;
let cloned = snapshot;
assert_eq!(copied.bundle_send_total, 3);
assert_eq!(cloned.bundle_partial_send, 1);
assert!(format!("{snapshot:?}").contains("GsoDiagnosticsSnapshot"));
}
#[test]
fn snapshot_serializes_with_stable_field_names() {
let snapshot = GsoDiagnosticsSnapshot {
bundle_send_total: 7,
bundle_partial_send: 2,
};
let json = serde_json::to_value(snapshot).expect("snapshot serializes");
assert_eq!(json["bundle_send_total"], 7);
assert_eq!(json["bundle_partial_send"], 2);
}
#[test]
fn diagnostics_debug_includes_counter_names() {
let diags = GsoDiagnostics::default();
let debug = format!("{diags:?}");
assert!(debug.contains("GsoDiagnostics"));
assert!(debug.contains("bundle_send_total"));
assert!(debug.contains("bundle_partial_send"));
}
}