use std::time::Duration;
use muxtop_core::process::{
SortField, SortOrder, build_process_tree, filter_processes, flatten_tree, sort_processes,
};
use muxtop_core::system::SystemSnapshot;
use tokio::sync::mpsc;
use tokio_util::sync::CancellationToken;
async fn collect_one_snapshot() -> SystemSnapshot {
use muxtop_core::collector::Collector;
let (tx, mut rx) = mpsc::channel(4);
let token = CancellationToken::new();
let collector = Collector::new(Duration::from_secs(1));
let handle = collector.spawn(tx, token.clone());
let snapshot = tokio::time::timeout(Duration::from_secs(4), rx.recv())
.await
.expect("timeout waiting for snapshot")
.expect("channel closed");
token.cancel();
handle.await.expect("collector panicked");
snapshot
}
#[tokio::test]
async fn test_full_pipeline_collect_sort_filter() {
let snapshot = collect_one_snapshot().await;
assert!(!snapshot.processes.is_empty(), "no processes collected");
assert!(!snapshot.cpu.cores.is_empty(), "no CPU cores");
assert!(snapshot.memory.total > 0, "no memory info");
let mut procs = snapshot.processes.clone();
sort_processes(&mut procs, SortField::Cpu, SortOrder::Desc);
for w in procs.windows(2) {
assert!(
w[0].cpu_percent >= w[1].cpu_percent,
"sort by CPU desc failed: {} < {}",
w[0].cpu_percent,
w[1].cpu_percent,
);
}
let filtered = filter_processes(&snapshot.processes, "muxtop");
assert!(
filtered.len() <= snapshot.processes.len(),
"filter returned more than total"
);
}
#[tokio::test]
async fn test_full_pipeline_tree_build() {
let snapshot = collect_one_snapshot().await;
let tree = build_process_tree(&snapshot.processes);
assert!(!tree.is_empty(), "tree should have root nodes");
for root in &tree {
assert_eq!(root.depth, 0, "root node should have depth 0");
}
let flat = flatten_tree(&tree);
assert_eq!(
flat.len(),
snapshot.processes.len(),
"flatten should preserve all processes"
);
}
#[tokio::test]
async fn test_network_in_full_pipeline() {
let snapshot = collect_one_snapshot().await;
assert!(
!snapshot.networks.interfaces.is_empty(),
"snapshot should have network interfaces"
);
assert_eq!(
snapshot.networks.total_rx,
snapshot
.networks
.interfaces
.iter()
.map(|i| i.bytes_rx)
.sum::<u64>(),
"total_rx should be consistent"
);
assert_eq!(
snapshot.networks.total_tx,
snapshot
.networks
.interfaces
.iter()
.map(|i| i.bytes_tx)
.sum::<u64>(),
"total_tx should be consistent"
);
for iface in &snapshot.networks.interfaces {
assert!(!iface.name.is_empty(), "interface name should not be empty");
}
}
#[tokio::test]
async fn test_full_pipeline_actions() {
let result = muxtop_core::actions::kill_process(u32::MAX, muxtop_core::actions::Signal::Term);
assert!(
matches!(result, Err(muxtop_core::CoreError::ProcessNotFound { .. })),
"kill(u32::MAX) must return ProcessNotFound: {result:?}"
);
}
#[tokio::test]
async fn test_full_pipeline_shutdown() {
use muxtop_core::collector::Collector;
let (tx, mut rx) = mpsc::channel(4);
let token = CancellationToken::new();
let collector = Collector::new(Duration::from_secs(1));
let handle = collector.spawn(tx, token.clone());
let _ = tokio::time::timeout(Duration::from_secs(4), rx.recv())
.await
.expect("timeout")
.expect("closed");
token.cancel();
let result = tokio::time::timeout(Duration::from_secs(2), handle).await;
assert!(result.is_ok(), "collector should shut down within 2s");
assert!(result.unwrap().is_ok(), "collector should not panic");
}