Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/dwarves

crates/dwarves-tune-graph/tests/cli.rs

//! The `dwarves-tune-graph` command: the cross-check of a Graph JSON file
//! (from the owner's possible-syscalls engine) with a probe file.

use std::path::Path;
use std::process::Command;

const BIN: &str = env!("CARGO_BIN_EXE_dwarves-tune-graph");

const GRAPH: &str = r#"{
  "binary": "/usr/bin/app",
  "arch": "x86_64",
  "modules": [{"name": "app", "path": "/usr/bin/app", "reached_functions": 3}],
  "entries": ["main"],
  "syscalls": {
    "read":  {"nr": 0, "direct": true, "path": ["main", "load"], "site": "load+0x10", "sites": 1},
    "write": {"nr": 1, "direct": true, "path": ["main"], "site": "main+0x20", "sites": 2},
    "pivot_root": {"nr": 155, "direct": false, "path": ["main", "via_pointer"], "site": "via_pointer+0x4", "sites": 1}
  },
  "unnamed": [],
  "unresolved_sites": [],
  "missing_libs": [],
  "unresolved_imports": [],
  "dlopen": false,
  "indirect_sites": 1
}"#;

const PROBE: &str = r#"{
  "workload": "app",
  "runs": [{"name": "drive/tpcds", "error_path": false}, {"name": "drive/bad-query", "error_path": true}],
  "calls": [
    {"syscall": "read", "runs": ["drive/tpcds"]},
    {"syscall": "sendto", "runs": ["drive/tpcds"]},
    {"syscall": "write", "runs": ["drive/bad-query"]}
  ],
  "unknown": []
}"#;

fn files(dir: &Path) -> (std::path::PathBuf, std::path::PathBuf) {
    let g = dir.join("graph.json");
    let p = dir.join("x86_64-glibc.probe.json");
    std::fs::write(&g, GRAPH).unwrap();
    std::fs::write(&p, PROBE).unwrap();
    (g, p)
}

#[test]
fn a_graph_file_is_cross_checked_with_the_probe_as_text() {
    let d = tempfile::tempdir().unwrap();
    let (g, p) = files(d.path());
    let out = Command::new(BIN).arg("--graph").arg(&g).arg("--profile").arg(&p).output().unwrap();
    let text = String::from_utf8_lossy(&out.stdout);
    assert!(out.status.success(), "{text}\n{}", String::from_utf8_lossy(&out.stderr));
    assert!(text.contains("graph cross-check"), "{text}");
    assert!(text.contains("never widens a filter"), "{text}");
    assert!(text.contains("sendto"), "the gap is named: {text}");
    assert!(text.contains("pivot_root"), "the untested path is named: {text}");
}

#[test]
fn json_gives_the_report_document() {
    let d = tempfile::tempdir().unwrap();
    let (g, p) = files(d.path());
    let out = Command::new(BIN)
        .arg("--graph")
        .arg(&g)
        .arg("--profile")
        .arg(&p)
        .arg("--json")
        .output()
        .unwrap();
    assert!(out.status.success(), "{}", String::from_utf8_lossy(&out.stderr));
    let r: dwarves_tune_graph::Report = serde_json::from_slice(&out.stdout).unwrap();
    assert_eq!(r.format, dwarves_tune_graph::REPORT_FORMAT);
    assert_eq!(r.confirmed, ["read", "write"]);
    assert_eq!(r.gaps.len(), 1);
    assert_eq!(r.gaps[0].syscall, "sendto");
    assert_eq!(r.untested.len(), 1);
    assert_eq!(r.untested[0].syscall, "pivot_root");
    assert_eq!(r.near_miss.len(), 1);
    assert_eq!(r.near_miss[0].syscall, "write");
    assert!(r.guides_only);
}

#[test]
fn a_binary_without_a_graph_file_is_refused_naming_graph() {
    let d = tempfile::tempdir().unwrap();
    let (_, p) = files(d.path());
    let out = Command::new(BIN).arg("/usr/bin/true").arg("--profile").arg(&p).output().unwrap();
    assert_eq!(out.status.code(), Some(2));
    let e = String::from_utf8_lossy(&out.stderr);
    assert!(e.contains("--graph"), "{e}");
}

#[test]
fn a_malformed_graph_is_a_usage_error_naming_the_file() {
    let d = tempfile::tempdir().unwrap();
    let (g, p) = files(d.path());
    std::fs::write(&g, "{\"binary\": 3}").unwrap();
    let out = Command::new(BIN).arg("--graph").arg(&g).arg("--profile").arg(&p).output().unwrap();
    assert_eq!(out.status.code(), Some(2));
    let e = String::from_utf8_lossy(&out.stderr);
    assert!(e.contains("graph.json"), "{e}");
}

#[test]
fn a_missing_profile_flag_is_a_usage_error() {
    let d = tempfile::tempdir().unwrap();
    let (g, _) = files(d.path());
    let out = Command::new(BIN).arg("--graph").arg(&g).output().unwrap();
    assert_eq!(out.status.code(), Some(2));
    assert!(String::from_utf8_lossy(&out.stderr).contains("--profile"));
}