#![cfg(feature = "ptop")]
use presentar_terminal::direct::CellBuffer;
use presentar_terminal::ptop::{App, PanelType};
use std::collections::HashMap;
fn find_k10temp_hwmon_path() -> Option<std::path::PathBuf> {
let entries = std::fs::read_dir("/sys/class/hwmon").ok()?;
for entry in entries.flatten() {
let name_path = entry.path().join("name");
if let Ok(name) = std::fs::read_to_string(&name_path) {
if name.trim() == "k10temp" {
return Some(entry.path());
}
}
}
None
}
fn print_available_temps(hwmon_path: &std::path::Path) {
println!("Available temps:");
for i in 1..=10 {
let path = hwmon_path.join(format!("temp{}_input", i));
if path.exists() {
let label_path = hwmon_path.join(format!("temp{}_label", i));
let label = std::fs::read_to_string(&label_path).unwrap_or_default();
println!(" temp{}_input exists (label: {})", i, label.trim());
}
}
}
fn count_zero_temps(temps: &[f32]) -> usize {
temps.iter().filter(|&&t| t == 0.0).count()
}
#[test]
fn falsify_temp_amd_k10temp_no_temp2() {
let Some(hwmon_path) = find_k10temp_hwmon_path() else {
println!("SKIP: Not an AMD k10temp system");
return;
};
let temp2_exists = hwmon_path.join("temp2_input").exists();
if !temp2_exists {
println!("CONFIRMED: temp2_input does NOT exist (k10temp layout)");
print_available_temps(&hwmon_path);
let app = App::with_config(false, Default::default());
let core0_temp = app.per_core_temp.get(0).copied().unwrap_or(0.0);
assert!(
core0_temp > 0.0,
"FALSIFIED: Core 0 has no temperature (got {}).\n\
Root cause: Code tried to read temp2_input which doesn't exist.\n\
Fix: Read temp*_label files to discover actual sensor layout.",
core0_temp
);
let zeros = count_zero_temps(&app.per_core_temp);
let zero_ratio = zeros as f32 / app.per_core_temp.len() as f32;
assert!(
zero_ratio < 0.1,
"FALSIFIED: {}/{} cores ({:.1}%) have no temperature.\n\
On k10temp, Tccd1-4 should be mapped to core groups.\n\
Expected: Cores 0-11 -> Tccd1, 12-23 -> Tccd2, etc.",
zeros,
app.per_core_temp.len(),
zero_ratio * 100.0
);
}
}
#[test]
fn falsify_temp_physically_implausible() {
let app = App::with_config(false, Default::default());
let mut out_of_range = Vec::new();
for (i, &temp) in app.per_core_temp.iter().enumerate() {
if temp == 0.0 {
continue; }
if temp < 15.0 || temp > 110.0 {
out_of_range.push((i, temp));
}
}
assert!(
out_of_range.is_empty(),
"FALSIFIED: {} cores have implausible temperatures: {:?}\n\
Valid range is 15-110C.",
out_of_range.len(),
&out_of_range[..out_of_range.len().min(5)]
);
}
#[test]
fn falsify_temp_vs_sensors_command() {
use std::process::Command;
let output = Command::new("sensors").output();
let Ok(output) = output else {
println!("SKIP: sensors command not available");
return;
};
let sensors_output = String::from_utf8_lossy(&output.stdout);
let mut tccd_temps: HashMap<String, f32> = HashMap::new();
for line in sensors_output.lines() {
if line.contains("Tccd") {
let parts: Vec<&str> = line.split(':').collect();
if parts.len() >= 2 {
let label = parts[0].trim().to_string();
let temp_str = parts[1]
.trim()
.trim_start_matches('+')
.split('°')
.next()
.unwrap_or("0");
if let Ok(temp) = temp_str.parse::<f32>() {
tccd_temps.insert(label, temp);
}
}
}
}
if tccd_temps.is_empty() {
println!("SKIP: No Tccd temperatures found in sensors output");
return;
}
println!("Sensors reports: {:?}", tccd_temps);
let app = App::with_config(false, Default::default());
if let Some(&sensors_tccd1) = tccd_temps.get("Tccd1") {
let our_core0_temp = app.per_core_temp.get(0).copied().unwrap_or(0.0);
if our_core0_temp > 0.0 {
let delta = (our_core0_temp - sensors_tccd1).abs();
assert!(
delta < 10.0,
"FALSIFIED: Core 0 temp {:.1}C differs from sensors Tccd1 {:.1}C by {:.1}C.",
our_core0_temp,
sensors_tccd1,
delta
);
}
}
}
#[test]
fn falsify_user_column_shows_dashes() {
let app = App::with_config(false, Default::default());
let processes: Vec<_> = app.system.processes().iter().collect();
assert!(!processes.is_empty(), "FALSIFIED: No processes found.");
let mut dash_count = 0;
let mut total_count = 0;
for (pid, process) in processes.iter().take(100) {
total_count += 1;
let user = process
.user_id()
.and_then(|uid| app.users.get_user_by_id(uid))
.map(|u| u.name().to_string());
if user.is_none() {
dash_count += 1;
if dash_count <= 3 {
println!("PID {} has no user (would show '-')", pid.as_u32());
}
}
}
let dash_ratio = dash_count as f32 / total_count as f32;
assert!(
dash_ratio < 0.10,
"FALSIFIED: {:.1}% of processes ({}/{}) have no user.\n\
.with_user() may be missing from ProcessRefreshKind.",
dash_ratio * 100.0,
dash_count,
total_count
);
}
#[test]
fn falsify_username_is_garbage() {
let app = App::with_config(false, Default::default());
for (_pid, process) in app.system.processes().iter().take(100) {
if let Some(uid) = process.user_id() {
if let Some(user) = app.users.get_user_by_id(uid) {
let name = user.name();
assert!(
name.chars()
.all(|c| c.is_alphanumeric() || c == '_' || c == '-' || c == '$'),
"FALSIFIED: Username '{}' contains invalid characters.",
name
);
assert!(!name.is_empty(), "FALSIFIED: Empty username found.");
assert!(
name.len() <= 32,
"FALSIFIED: Username '{}' is {} chars (max 32).",
name,
name.len()
);
}
}
}
}
#[test]
fn falsify_command_is_garbled() {
let app = App::with_config(false, Default::default());
for (pid, process) in app.system.processes().iter().take(100) {
let cmd = process.name().to_string_lossy();
let has_control = cmd.chars().any(|c| c.is_control() && c != '\t');
assert!(
!has_control,
"FALSIFIED: PID {} command '{}' contains control characters.",
pid.as_u32(),
cmd
);
assert!(
!cmd.contains('\0'),
"FALSIFIED: PID {} command contains null byte.",
pid.as_u32()
);
}
}
#[test]
fn falsify_core_count_mismatch() {
let expected_cores = std::fs::read_to_string("/proc/cpuinfo")
.unwrap_or_default()
.matches("processor")
.count();
let app = App::with_config(false, Default::default());
assert_eq!(
app.per_core_percent.len(),
expected_cores,
"FALSIFIED: per_core_percent has {} entries, expected {}.",
app.per_core_percent.len(),
expected_cores
);
assert_eq!(
app.per_core_freq.len(),
expected_cores,
"FALSIFIED: per_core_freq has {} entries, expected {}.",
app.per_core_freq.len(),
expected_cores
);
assert_eq!(
app.per_core_temp.len(),
expected_cores,
"FALSIFIED: per_core_temp has {} entries, expected {}.",
app.per_core_temp.len(),
expected_cores
);
}
#[test]
fn falsify_cores_not_visible() {
use presentar_terminal::ptop::ui;
let expected_cores = std::fs::read_to_string("/proc/cpuinfo")
.unwrap_or_default()
.matches("processor")
.count();
let mut app = App::with_config(false, Default::default());
app.exploded_panel = Some(PanelType::Cpu);
let mut buffer = CellBuffer::new(180, 60);
ui::draw(&app, &mut buffer);
let mut output = String::new();
for y in 0..60 {
for x in 0..180 {
if let Some(cell) = buffer.get(x, y) {
output.push(cell.symbol.chars().next().unwrap_or(' '));
}
}
output.push('\n');
}
let mut visible_cores = std::collections::HashSet::new();
for line in output.lines() {
for word in line.split_whitespace() {
if let Ok(num) = word.parse::<usize>() {
if num < expected_cores {
visible_cores.insert(num);
}
}
}
}
let visible_ratio = visible_cores.len() as f32 / expected_cores as f32;
assert!(
visible_ratio >= 0.5,
"FALSIFIED: Only {}/{} cores visible ({:.1}%).",
visible_cores.len(),
expected_cores,
visible_ratio * 100.0
);
}
#[test]
fn falsify_freq_vs_proc_cpuinfo() {
let cpuinfo = std::fs::read_to_string("/proc/cpuinfo").unwrap_or_default();
let actual_freqs: Vec<f64> = cpuinfo
.lines()
.filter(|line| line.starts_with("cpu MHz"))
.filter_map(|line| line.split(':').nth(1).and_then(|s| s.trim().parse().ok()))
.collect();
if actual_freqs.is_empty() {
println!("SKIP: Could not read frequencies from /proc/cpuinfo");
return;
}
let app = App::with_config(false, Default::default());
for (i, &actual_mhz) in actual_freqs.iter().enumerate().take(8) {
let our_mhz = app.per_core_freq.get(i).copied().unwrap_or(0) as f64;
if our_mhz == 0.0 {
continue;
}
let delta = (our_mhz - actual_mhz).abs();
assert!(
delta < 500.0,
"FALSIFIED: Core {} freq {:.0}MHz differs from /proc/cpuinfo {:.0}MHz by {:.0}MHz.",
i,
our_mhz,
actual_mhz,
delta
);
}
}
#[test]
fn falsify_freq_physically_implausible() {
let app = App::with_config(false, Default::default());
for (i, &freq_mhz) in app.per_core_freq.iter().enumerate() {
if freq_mhz == 0 {
continue;
}
assert!(
freq_mhz >= 400,
"FALSIFIED: Core {} freq {}MHz is below 400MHz.",
i,
freq_mhz
);
assert!(
freq_mhz <= 6500,
"FALSIFIED: Core {} freq {}MHz exceeds 6.5GHz.",
i,
freq_mhz
);
}
}
#[test]
fn falsify_load_avg_vs_proc() {
let loadavg = std::fs::read_to_string("/proc/loadavg").unwrap_or_default();
let parts: Vec<&str> = loadavg.split_whitespace().collect();
if parts.len() < 3 {
println!("SKIP: Could not parse /proc/loadavg");
return;
}
let actual_1min: f64 = parts[0].parse().unwrap_or(0.0);
let app = App::with_config(false, Default::default());
let delta_1 = (app.load_avg.one - actual_1min).abs();
assert!(
delta_1 < 5.0,
"FALSIFIED: 1min load avg {:.2} differs from /proc/loadavg {:.2} by {:.2}.",
app.load_avg.one,
actual_1min,
delta_1
);
}
#[test]
fn falsify_memory_total_vs_proc() {
let meminfo = std::fs::read_to_string("/proc/meminfo").unwrap_or_default();
let actual_total_kb: u64 = meminfo
.lines()
.find(|line| line.starts_with("MemTotal:"))
.and_then(|line| line.split_whitespace().nth(1).and_then(|s| s.parse().ok()))
.unwrap_or(0);
if actual_total_kb == 0 {
println!("SKIP: Could not parse /proc/meminfo");
return;
}
let actual_total_bytes = actual_total_kb * 1024;
let app = App::with_config(false, Default::default());
let our_total = app.system.total_memory();
let delta_ratio =
(our_total as f64 - actual_total_bytes as f64).abs() / actual_total_bytes as f64;
assert!(
delta_ratio < 0.01,
"FALSIFIED: Memory total {} differs from /proc/meminfo {} by {:.2}%.",
our_total,
actual_total_bytes,
delta_ratio * 100.0
);
}
#[test]
fn falsify_memory_math() {
let app = App::with_config(false, Default::default());
let total = app.system.total_memory();
let used = app.system.used_memory();
let available = app.system.available_memory();
let sum = used + available;
let delta_ratio = (sum as f64 - total as f64).abs() / total as f64;
assert!(
delta_ratio < 0.15,
"FALSIFIED: used + available = {} differs from total {} by {:.1}%.",
sum,
total,
delta_ratio * 100.0
);
}
#[test]
fn falsify_placeholder_text_in_output() {
use presentar_terminal::ptop::ui;
let app = App::with_config(false, Default::default());
let mut buffer = CellBuffer::new(140, 45);
ui::draw(&app, &mut buffer);
let mut output = String::new();
for y in 0..45 {
for x in 0..140 {
if let Some(cell) = buffer.get(x, y) {
output.push(cell.symbol.chars().next().unwrap_or(' '));
}
}
output.push('\n');
}
let placeholders = ["TODO", "PLACEHOLDER", "FIXME", "XXX"];
for placeholder in placeholders {
assert!(
!output.contains(placeholder),
"FALSIFIED: Output contains placeholder text '{}'.",
placeholder
);
}
}
#[test]
fn falsify_deterministic_not_zero() {
let app = App::with_config(true, Default::default());
assert!(
app.per_core_percent.iter().all(|&p| p == 0.0),
"FALSIFIED: Deterministic mode has non-zero CPU percentages."
);
assert!(
app.per_core_freq.iter().all(|&f| f == 0),
"FALSIFIED: Deterministic mode has non-zero frequencies."
);
assert!(
app.load_avg.one == 0.0 && app.load_avg.five == 0.0 && app.load_avg.fifteen == 0.0,
"FALSIFIED: Deterministic mode has non-zero load average."
);
}
#[test]
fn falsify_filter_test_is_fraudulent() {
println!("WARNING: test_app_filter_processes in app.rs is FRAUDULENT");
println!("It tests string assignment, NOT process filtering.");
}
#[test]
fn falsify_filter_does_not_reduce_count() {
use presentar_terminal::ptop::App;
let mut app = App::with_config(false, Default::default());
let total_processes = app.system.processes().len();
if total_processes < 10 {
println!("SKIP: Too few processes to test filtering");
return;
}
app.filter = "XYZNONEXISTENT".to_string();
let filtered_count = app.process_count();
assert!(
filtered_count < total_processes,
"FALSIFIED: Filter '{}' did not reduce process count.\n\
Total: {}, Filtered: {}\n\
The filter field is stored but NOT USED for filtering.",
app.filter,
total_processes,
filtered_count
);
assert!(
filtered_count <= 5,
"FALSIFIED: Filter 'XYZNONEXISTENT' still returns {} processes.\n\
Expected: 0-5 (in case of coincidental match).",
filtered_count
);
}
#[test]
fn falsify_filter_does_not_match_known_process() {
use presentar_terminal::ptop::App;
let mut app = App::with_config(false, Default::default());
let known_process = app.system.processes().values().find(|p| {
let name = p.name().to_string_lossy().to_lowercase();
name.len() > 3 && !name.contains(' ')
});
let Some(proc) = known_process else {
println!("SKIP: Could not find suitable process for testing");
return;
};
let proc_name = proc.name().to_string_lossy().to_string();
let search_term = proc_name.chars().take(4).collect::<String>().to_lowercase();
println!(
"Testing filter with known process: {} (searching for '{}')",
proc_name, search_term
);
app.filter = search_term.clone();
let filtered_count = app.process_count();
assert!(
filtered_count >= 1,
"FALSIFIED: Filter '{}' found 0 matches.\n\
Process '{}' should have matched.\n\
Filter is NOT being applied correctly.",
search_term,
proc_name
);
}
#[test]
fn falsify_autocomplete_exists() {
println!("DOCUMENTED: No autocomplete feature exists in ptop.");
println!("Grep for 'autocomplete|auto_complete|auto-complete' returns 0 matches.");
println!("If autocomplete was advertised, it is FALSE ADVERTISING.");
}
#[test]
fn falsify_filter_not_visible_in_ui() {
use presentar_terminal::direct::CellBuffer;
use presentar_terminal::ptop::{ui, App};
let mut app = App::with_config(true, Default::default()); app.filter = "testfilter".to_string();
app.show_filter_input = true;
let mut buffer = CellBuffer::new(140, 45);
ui::draw(&app, &mut buffer);
let mut output = String::new();
for y in 0..45 {
for x in 0..140 {
if let Some(cell) = buffer.get(x, y) {
output.push(cell.symbol.chars().next().unwrap_or(' '));
}
}
output.push('\n');
}
assert!(
output.contains("testfilter") || output.contains("Filter"),
"FALSIFIED: Filter text not visible in UI.\n\
Filter value: '{}'\n\
show_filter_input: true\n\
The filter overlay is NOT being rendered.",
app.filter
);
}
#[test]
fn falsify_request_signal_sets_pending() {
use crossterm::event::KeyCode;
use presentar_terminal::ptop::App;
let mut app = App::with_config(false, Default::default());
app.focused_panel = Some(PanelType::Process);
let processes: Vec<_> = app.system.processes().iter().collect();
if processes.is_empty() {
println!("SKIP: No processes available for testing");
return;
}
app.process_selected = 0;
app.handle_key(KeyCode::Char('x'), crossterm::event::KeyModifiers::empty());
assert!(
app.pending_signal.is_some(),
"FALSIFIED: request_signal did not set pending_signal.\n\
After pressing 'x' with a selected process, pending_signal should be Some.\n\
Current: {:?}",
app.pending_signal
);
if let Some((pid, name, signal_type)) = &app.pending_signal {
assert!(*pid > 0, "PID should be positive");
assert!(!name.is_empty(), "Process name should not be empty");
println!(
"Signal request created: PID={}, name={}, signal={:?}",
pid, name, signal_type
);
}
}
#[test]
fn falsify_signal_hotkeys_work() {
use crossterm::event::{KeyCode, KeyModifiers};
use presentar_terminal::ptop::config::SignalType;
use presentar_terminal::ptop::App;
let mut app = App::with_config(false, Default::default());
app.focused_panel = Some(PanelType::Process);
let processes: Vec<_> = app.system.processes().iter().collect();
if processes.is_empty() {
println!("SKIP: No processes available for testing");
return;
}
app.process_selected = 0;
app.handle_key(KeyCode::Char('x'), KeyModifiers::empty());
if let Some((_, _, signal)) = &app.pending_signal {
assert!(
matches!(signal, SignalType::Term),
"FALSIFIED: 'x' should request TERM, got {:?}",
signal
);
}
app.cancel_signal();
app.handle_key(KeyCode::Char('k'), KeyModifiers::empty());
if let Some((_, _, signal)) = &app.pending_signal {
assert!(
matches!(signal, SignalType::Kill),
"FALSIFIED: 'k' should request KILL, got {:?}",
signal
);
}
}
#[test]
fn falsify_signal_requires_confirmation() {
use crossterm::event::{KeyCode, KeyModifiers};
use presentar_terminal::ptop::App;
let mut app = App::with_config(false, Default::default());
app.focused_panel = Some(PanelType::Process);
let processes: Vec<_> = app.system.processes().iter().collect();
if processes.is_empty() {
println!("SKIP: No processes available for testing");
return;
}
app.process_selected = 0;
app.handle_key(KeyCode::Char('x'), KeyModifiers::empty());
assert!(
app.signal_result.is_none(),
"FALSIFIED: Signal was sent without confirmation!"
);
app.handle_key(KeyCode::Char('n'), KeyModifiers::empty());
assert!(
app.pending_signal.is_none(),
"FALSIFIED: 'n' did not cancel pending signal"
);
app.handle_key(KeyCode::Char('x'), KeyModifiers::empty());
app.handle_key(KeyCode::Esc, KeyModifiers::empty());
}
#[test]
fn falsify_sorted_processes_ignores_filter() {
use presentar_terminal::ptop::App;
let mut app = App::with_config(false, Default::default());
let total = app.sorted_processes().len();
if total < 10 {
println!("SKIP: Too few processes to test filtering");
return;
}
app.filter = "XYZNONEXISTENT".to_string();
let filtered = app.sorted_processes().len();
assert!(
filtered < total,
"FALSIFIED: sorted_processes() does not apply filter.\n\
Total: {}, After filter '{}': {}\n\
Filter is ignored by sorted_processes().",
total,
app.filter,
filtered
);
}
#[test]
fn falsify_psi_detection() {
use presentar_terminal::ptop::App;
let app = App::with_config(false, Default::default());
let psi_available = std::path::Path::new("/proc/pressure/cpu").exists();
let data_avail = app.data_availability();
if psi_available {
println!("System has PSI support (/proc/pressure/cpu exists)");
} else {
assert!(
!data_avail.psi_available,
"FALSIFIED: has_psi() returns true but /proc/pressure/cpu doesn't exist"
);
}
}
#[test]
fn falsify_sensor_detection() {
use presentar_terminal::ptop::App;
let app = App::with_config(false, Default::default());
let hwmon_exists = std::path::Path::new("/sys/class/hwmon")
.read_dir()
.map(|mut d| d.next().is_some())
.unwrap_or(false);
let data_avail = app.data_availability();
if !hwmon_exists {
assert!(
!data_avail.sensors_available,
"FALSIFIED: has_sensors() returns true but no hwmon devices exist"
);
} else {
println!("System has hwmon devices, sensor detection should work");
}
}
#[test]
fn falsify_connections_detection() {
use presentar_terminal::ptop::App;
let app = App::with_config(false, Default::default());
let tcp_available = std::path::Path::new("/proc/net/tcp").exists();
let data_avail = app.data_availability();
if tcp_available {
println!("System has /proc/net/tcp, connection tracking should work");
} else {
assert!(
!data_avail.connections_available,
"FALSIFIED: has_connections() returns true but /proc/net/tcp doesn't exist"
);
}
}
#[test]
fn report_test_severities() {
println!("\n=== FALSIFICATION TEST SEVERITY REPORT ===");
println!("falsify_temp_amd_k10temp_no_temp2 : S4");
println!("falsify_temp_physically_implausible : S4");
println!("falsify_temp_vs_sensors_command : S4");
println!("falsify_user_column_shows_dashes : S4");
println!("falsify_username_is_garbage : S4");
println!("falsify_command_is_garbled : S4");
println!("falsify_core_count_mismatch : S4");
println!("falsify_cores_not_visible : S4");
println!("falsify_freq_vs_proc_cpuinfo : S4");
println!("falsify_freq_physically_implausible : S3");
println!("falsify_load_avg_vs_proc : S4");
println!("falsify_memory_total_vs_proc : S4");
println!("falsify_memory_math : S3");
println!("falsify_placeholder_text_in_output : S4");
println!("falsify_deterministic_not_zero : S4");
println!("falsify_filter_test_is_fraudulent : S4");
println!("falsify_filter_does_not_reduce_count : S4");
println!("falsify_filter_does_not_match_known : S4");
println!("falsify_autocomplete_exists : S4");
println!("falsify_filter_not_visible_in_ui : S4");
println!("falsify_request_signal_sets_pending : S4");
println!("falsify_signal_hotkeys_work : S4");
println!("falsify_signal_requires_confirmation : S4");
println!("falsify_sorted_processes_ignores_flt : S4");
println!("falsify_psi_detection : S3");
println!("falsify_sensor_detection : S3");
println!("falsify_connections_detection : S3");
println!("==========================================");
println!("Total: 27 falsification tests");
println!("S4: 22, S3: 5");
}