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

nordisk/nornir

src/code_room/reader/wired/trace_tests.rs

//! **A traced run lands on the map.** A filed chunk of a run over the fixture
//! room's `shop` (functions named by symbol and source location, the way a Runa
//! `Trace` step files them) is mapped to the room's functions, and the replay's
//! `Viz.CodeMap` lods answer from it: the runs with their mapped share, a window
//! of events on heads at the map's own positions, paged, with each thread's open
//! heads at a scrub point and the run's heat.

use nornir_trace::wire::{ChunkFiling, FiledFn};
use nornir_trace::{EventKind, TraceEvent};
use serde_json::{Value, json};

use crate::mimir_lens::room_feed::fixture::{self, SHOP};
use crate::warehouse::step_feed::{Measured, StepFeed, TraceChunk, file_mapped};

use super::trace::RoomTraceMap;

fn f(symbol: &str, repo: &str, file: &str, line: u32) -> FiledFn {
    FiledFn { symbol: symbol.into(), repo: repo.into(), file: file.into(), line }
}

fn map(ws: &fixture::Workspace, p: Value) -> Value {
    super::codemap::code_map(&ws.reach(), &p).unwrap_or_else(|r| panic!("{p}: [{}] {}", r.state, r.note))
}

/// The map position of the card `find` names `path` (the top middle of its rect).
fn head_of(ws: &fixture::Workspace, q: &str, path: &str) -> [f64; 2] {
    let v = map(ws, json!({ "lod": "find", "q": q }));
    let hit = v["hits"].as_array().or(v["results"].as_array()).into_iter().flatten().find(|h| h["what"] == "fn" && h["path"] == path).unwrap_or_else(|| panic!("no {path}: {v}")).clone();
    let r = hit["rect"].as_array().unwrap().iter().map(|x| x.as_f64().unwrap()).collect::<Vec<_>>();
    [(r[0] + r[2]) * 0.5, r[1]]
}

#[test]
fn a_filed_trace_maps_to_the_rooms_functions_and_replays_on_the_maps_heads() {
    let _g = fixture::serial();
    let ws = fixture::workspace_without_runs();
    let functions = vec![
        f("shop::pay::charge", SHOP, "src/pay.rs", 5),                     // 0: by span
        f("shop::cart::Cart::add::{closure#0}", SHOP, "src/cart.rs", 11),  // 1: a closure → add
        f("<shop::cart::Cart>::total", "", "", 0),                         // 2: by path only
        f("std::io::stdio::_print", "", "", 0),                            // 3: not in the room
        f("shop::pay::audit", SHOP, "src/pay.rs", 23),                     // 4: by span
    ];
    use EventKind::*;
    let evs = [(7, 0, 0, Enter), (9, 5, 1, Enter), (9, 6, 2, Enter), (9, 8, 2, Exit), (7, 10, 4, Enter), (9, 12, 1, Exit), (7, 15, 3, Enter), (7, 16, 3, Exit), (7, 20, 4, Exit), (7, 30, 0, Exit)];
    let events: Vec<TraceEvent> = evs.iter().enumerate().map(|(i, &(thread, t_ns, func, kind))| TraceEvent { thread, seq_no: i as u64, t_ns, func, kind }).collect();
    let filing = ChunkFiling { chunk: 0, chunks: 1, base_seq: 0, target: "test shop".into(), functions, total_events: 10, threads: 2, duration_ns: 30, ..Default::default() };
    let room = super::open(&ws.reach(), "").unwrap();
    let feed = StepFeed {
        repo: SHOP.into(),
        commit: "c1".into(),
        run_id: "run-trace-1".into(),
        ts_micros: chrono::Utc::now().timestamp_micros(),
        measured: Measured::Trace(Box::new(TraceChunk { filing, events, source: "runa ci run-1 #0 Trace".into() })),
        selection: None,
    };
    let rm = RoomTraceMap { room };
    assert_eq!(ws.warehouse.block_on(file_mapped(&ws.warehouse, &feed, &rm)).unwrap(), 10);
    assert_eq!(ws.warehouse.block_on(file_mapped(&ws.warehouse, &feed, &rm)).unwrap(), 0, "a chunk is filed once");

    // the runs: 4 of 5 functions are the room's, all 3 that live in a checkout
    let runs = map(&ws, json!({ "lod": "trace_runs", "repo": SHOP }));
    let r = &runs["runs"][0];
    assert_eq!((r["run_id"].as_str(), r["events"].as_u64(), r["threads"].as_u64()), (Some("run-trace-1"), Some(10), Some(2)), "{runs}");
    assert_eq!((r["functions"].as_u64(), r["mapped_functions"].as_u64(), r["mapped_share"].as_f64()), (Some(5), Some(4), Some(0.8)), "{r}");
    assert_eq!((r["local_functions"].as_u64(), r["mapped_local_functions"].as_u64()), (Some(3), Some(3)), "{r}");
    assert_eq!(r["mapped_events"].as_u64(), Some(8), "{r}");

    // a window: placed events on heads at the map's positions, std counted unplaced
    let w = map(&ws, json!({ "lod": "trace", "run_id": "run-trace-1", "from_ns": 0, "to_ns": 16 }));
    let events = w["events"].as_array().unwrap();
    let heads = w["heads"].as_array().unwrap();
    let path_of = |e: &Value| heads[e[4].as_u64().unwrap() as usize]["path"].as_str().unwrap().to_string();
    let got: Vec<(u64, u64, String, u64)> = events.iter().map(|e| (e[1].as_u64().unwrap(), e[2].as_u64().unwrap(), path_of(e), e[3].as_u64().unwrap())).collect();
    assert_eq!(
        got,
        vec![
            (0, 7, "shop::pay::charge".into(), 0),
            (5, 9, "shop::cart::Cart::add".into(), 0),
            (6, 9, "shop::cart::Cart::total".into(), 0),
            (8, 9, "shop::cart::Cart::total".into(), 1),
            (10, 7, "shop::pay::audit".into(), 0),
            (12, 9, "shop::cart::Cart::add".into(), 1),
        ],
        "{w}"
    );
    assert_eq!(w["unplaced"], 2, "{w}");
    // an entry names its caller: the innermost placed function open on its thread
    let caller_of = |t: u64| events.iter().find(|e| e[1] == t && e[3] == 0).and_then(|e| e[5].as_u64()).map(|h| heads[h as usize]["path"].as_str().unwrap().to_string());
    assert_eq!(caller_of(0), None, "the outermost entry has no caller");
    assert_eq!(caller_of(6).as_deref(), Some("shop::cart::Cart::add"), "total is called by add on thread 9");
    assert_eq!(caller_of(10).as_deref(), Some("shop::pay::charge"), "audit is called by charge on thread 7");
    for h in heads {
        let at = [h["at"][0].as_f64().unwrap(), h["at"][1].as_f64().unwrap()];
        let name = h["path"].as_str().unwrap().rsplit("::").next().unwrap();
        assert_eq!(at, head_of(&ws, name, h["path"].as_str().unwrap()), "{h}");
    }

    // paged
    let p1 = map(&ws, json!({ "lod": "trace", "run_id": "run-trace-1", "limit": 2 }));
    assert_eq!((p1["events"].as_array().unwrap().len(), p1["more"].as_bool()), (2, Some(true)), "{p1}");
    let p2 = map(&ws, json!({ "lod": "trace", "run_id": "run-trace-1", "limit": 2, "after_seq": p1["next_after_seq"] }));
    assert_eq!(p2["events"][0][1], 6, "the page after seq 1 starts at total's entry: {p2}");
    let p2h = |e: &Value| p2["heads"][e.as_u64().unwrap() as usize]["path"].as_str().unwrap().to_string();
    assert_eq!(p2h(&p2["events"][0][5]), "shop::cart::Cart::add", "a later page knows the caller from the run before it: {p2}");

    // a scrub to t=7: who is open on which thread; and the run's heat
    let s = map(&ws, json!({ "lod": "trace", "run_id": "run-trace-1", "from_ns": 7, "to_ns": 7, "stacks": true, "heat": true }));
    let hp = |i: &Value| s["heads"][i.as_u64().unwrap() as usize]["path"].as_str().unwrap().to_string();
    let stacks: Vec<(u64, Vec<String>)> = s["stacks"].as_array().unwrap().iter().map(|t| (t["thread"].as_u64().unwrap(), t["heads"].as_array().unwrap().iter().map(hp).collect())).collect();
    assert_eq!(stacks, vec![(7, vec!["shop::pay::charge".to_string()]), (9, vec!["shop::cart::Cart::add".into(), "shop::cart::Cart::total".into()])], "{s}");
    let heat: Vec<(String, u64)> = s["heat"].as_array().unwrap().iter().map(|h| (hp(&h[0]), h[1].as_u64().unwrap())).collect();
    assert_eq!(heat, vec![("shop::pay::charge".into(), 30), ("shop::pay::audit".into(), 10), ("shop::cart::Cart::add".into(), 7), ("shop::cart::Cart::total".into(), 2)], "{s}");
}