|
| 1 | +use std::collections::HashSet; |
| 2 | +use std::path::PathBuf; |
| 3 | +use std::process::Command; |
| 4 | + |
| 5 | +const KNOWN_FALCO_FAILURES: &[&str] = &[ |
| 6 | + // libbpf poison/dummy path leaves a dead def in these large programs |
| 7 | + "prog195.txt", |
| 8 | + "prog198.txt", |
| 9 | + // RA post-spill fallback currently hits convergence cap / repeated pre-spills |
| 10 | + "prog280.txt", |
| 11 | + "prog286.txt", |
| 12 | +]; |
| 13 | + |
| 14 | +fn have_timeout() -> bool { |
| 15 | + Command::new("sh") |
| 16 | + .arg("-c") |
| 17 | + .arg("command -v timeout >/dev/null 2>&1") |
| 18 | + .status() |
| 19 | + .map(|s| s.success()) |
| 20 | + .unwrap_or(false) |
| 21 | +} |
| 22 | + |
| 23 | +#[test] |
| 24 | +fn falco_dump_corpus_rewrites_except_known_failures() { |
| 25 | + let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); |
| 26 | + let falco = manifest.parent().unwrap().join("bpftests/falco"); |
| 27 | + if !falco.exists() { |
| 28 | + eprintln!("skipping falco tests: {} does not exist", falco.display()); |
| 29 | + return; |
| 30 | + } |
| 31 | + |
| 32 | + let mut files: Vec<_> = std::fs::read_dir(&falco) |
| 33 | + .expect("read falco dir") |
| 34 | + .filter_map(|e| e.ok().map(|e| e.path())) |
| 35 | + .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("txt")) |
| 36 | + .collect(); |
| 37 | + files.sort(); |
| 38 | + |
| 39 | + if files.is_empty() { |
| 40 | + eprintln!("skipping falco tests: no .txt programs in {}", falco.display()); |
| 41 | + return; |
| 42 | + } |
| 43 | + |
| 44 | + let known: HashSet<&str> = KNOWN_FALCO_FAILURES.iter().copied().collect(); |
| 45 | + let epasstool = env!("CARGO_BIN_EXE_epasstool"); |
| 46 | + let out = std::env::temp_dir().join(format!("epass-falco-{}.dump", std::process::id())); |
| 47 | + let use_timeout = have_timeout(); |
| 48 | + |
| 49 | + let mut passed = 0usize; |
| 50 | + let mut known_failed = 0usize; |
| 51 | + let mut unexpected = Vec::new(); |
| 52 | + |
| 53 | + for file in files { |
| 54 | + let name = file.file_name().unwrap().to_string_lossy().to_string(); |
| 55 | + let output = if use_timeout { |
| 56 | + Command::new("timeout") |
| 57 | + .arg("-k") |
| 58 | + .arg("2") |
| 59 | + .arg("10") |
| 60 | + .arg(epasstool) |
| 61 | + .arg("read") |
| 62 | + .arg("-F") |
| 63 | + .arg("log") |
| 64 | + .arg("-o") |
| 65 | + .arg(&out) |
| 66 | + .arg(&file) |
| 67 | + .output() |
| 68 | + .expect("run epasstool with timeout") |
| 69 | + } else { |
| 70 | + Command::new(epasstool) |
| 71 | + .arg("read") |
| 72 | + .arg("-F") |
| 73 | + .arg("log") |
| 74 | + .arg("-o") |
| 75 | + .arg(&out) |
| 76 | + .arg(&file) |
| 77 | + .output() |
| 78 | + .expect("run epasstool") |
| 79 | + }; |
| 80 | + if output.status.success() { |
| 81 | + passed += 1; |
| 82 | + } else if known.contains(name.as_str()) { |
| 83 | + known_failed += 1; |
| 84 | + } else { |
| 85 | + unexpected.push(format!( |
| 86 | + "{}: rc={:?}\nstdout={}\nstderr={}", |
| 87 | + name, |
| 88 | + output.status.code(), |
| 89 | + String::from_utf8_lossy(&output.stdout), |
| 90 | + String::from_utf8_lossy(&output.stderr) |
| 91 | + )); |
| 92 | + } |
| 93 | + } |
| 94 | + let _ = std::fs::remove_file(&out); |
| 95 | + |
| 96 | + eprintln!( |
| 97 | + "falco corpus: passed={} known_failed={} unexpected_failed={}", |
| 98 | + passed, |
| 99 | + known_failed, |
| 100 | + unexpected.len() |
| 101 | + ); |
| 102 | + assert!(unexpected.is_empty(), "unexpected Falco failures:\n{}", unexpected.join("\n\n")); |
| 103 | +} |
0 commit comments