firedbg-rust-debugger 1.74.1

FireDBG Debugger Engine for Rust
Documentation
mod util;
use util::*;

use anyhow::Result;
use firedbg_rust_debugger::{Bytes, Debugger, EventStream};
use sea_streamer::{Buffer, Consumer, Message, Producer};

#[tokio::test]
async fn main() -> Result<()> {
    let testcase = "more_result";
    let (producer, consumer) = setup(testcase).await?;

    let tuple_ab = vec!["11i32".to_owned(), "12i32".to_owned()];
    let tuple_234 = "(2i32, 3i32, 4i64)".to_owned();
    let valid_str = "[0xf0, 0x9f, 0x92, 0x96]".to_owned();
    let invalid_str = "[0x00, 0x9f, 0x92, 0x96]".to_owned();
    let debugger_params = generate_rust_program(
        testcase,
        r#"
        fn res_1(a: i32, b: i32) -> (i32, i32, i64) {
            {tuple_234}
        }
        fn open_file_1() -> std::io::Result<()> {
            std::fs::File::open("/non-existent-file")?;
            Ok(())
        }
        fn open_file_2() -> std::io::Result<std::fs::File> {
            std::fs::File::open("/dev/urandom")
        }
        fn open_file_3() -> std::io::Result<std::fs::File> {
            std::fs::File::open("/non-existent-file")
        }
        fn str_from_utf8(b: &[u8]) -> Result<&str, std::str::Utf8Error> {
            std::str::from_utf8(b)
        }
        fn string_from_utf8(b: Vec<u8>) -> Result<String, std::string::FromUtf8Error> {
            String::from_utf8(b)
        }
        fn result_boxed(i: i32) -> Result<Box<String>, ()> {
            if i == 0 {
                Ok(Box::new(String::from("hello")))
            } else {
                Err(())
            }
        }
        fn passthru<T>(v: T) -> T {
            std::hint::black_box(v)
        }
        struct RNode {
            i: i32,
            next: Result<Box<RNode>, ()>,
        }
        fn main() {
            println!("hello");
            res_1({tuple_ab});
            assert!(open_file_1().is_err());
            assert!(open_file_2().is_ok());
            assert!(open_file_3().is_err());
            assert!(str_from_utf8(&{valid_str}).is_ok());
            assert!(str_from_utf8(&{invalid_str}).is_err());
            assert!(string_from_utf8(vec!{valid_str}).is_ok());
            assert!(string_from_utf8(vec!{invalid_str}).is_err());
            assert!(passthru(result_boxed(0)).is_ok());
            assert!(passthru(result_boxed(1)).is_err());

            let node = RNode {
                i: 1,
                next: Ok(Box::new(RNode {
                    i: 2,
                    next: Ok(Box::new(RNode {
                        i: 3,
                        next: Err(()),
                    })),
                }))
            };
            passthru(&node);
        }
        "#
        .replace("{tuple_234}", &tuple_234)
        .replace("{tuple_ab}", &tuple_ab.join(", "))
        .replace("{valid_str}", &valid_str)
        .replace("{invalid_str}", &invalid_str)
        .as_str(),
    );
    let result_boxed_ok = r#"core::result::Result::<alloc::boxed::Box<alloc::string::String>, ()>::Ok(alloc::boxed::Box::new(String::from("hello")))"#.to_owned();
    let result_boxed_err =
        r#"core::result::Result::<alloc::boxed::Box<alloc::string::String>, ()>::Err(())"#
            .to_owned();
    let result_chain =
        expand("&RNode { i: 1i32, next: Result<Box<RNode>, ()>::Ok(Box::new(RNode { i: 2i32, next: Result<Box<RNode>, ()>::Ok(Box::new(RNode { i: 3i32, next: Result<Box<RNode>, ()>::Err(()) })) })) }");
    Debugger::run(debugger_params, producer.clone());

    producer.end().await?;

    let expected = vec![
        Expected::FnCall {
            name: "main".into(),
            args: vec![],
        },
        Expected::FnCall {
            name: "res_1".into(),
            args: tuple_ab,
        },
        Expected::FnRet {
            name: "res_1".into(),
            value: tuple_234,
        },
        Expected::FnCall {
            name: "open_file_1".into(),
            args: vec![],
        },
        Expected::FnRet {
            name: "open_file_1".into(),
            value:
                "core::result::Result::<(), std::io::error::Error>::Err(std::io::error::Error { code: 2i32, kind: std::io::error::ErrorKind::NotFound, .. })"
                    .into(),
        },
        Expected::FnCall {
            name: "open_file_2".into(),
            args: vec![],
        },
        Expected::FnRet {
            name: "open_file_2".into(),
            value: "core::result::Result::<std::fs::File, std::io::error::Error>::Ok(std::fs::File {..})"
                .into(),
        },
        Expected::FnCall {
            name: "open_file_3".into(),
            args: vec![],
        },
        Expected::FnRet {
            name: "open_file_3".into(),
            value: "core::result::Result::<std::fs::File, std::io::error::Error>::Err(std::io::error::Error {..})"
                .into(),
        },
        Expected::FnCall {
            name: "str_from_utf8".into(),
            args: vec![format!("&{valid_str}")],
        },
        Expected::FnRet {
            name: "str_from_utf8".into(),
            value: "core::result::Result::<&str, core::str::error::Utf8Error>::Ok(\"💖\")"
                .into(),
        },
        Expected::FnCall {
            name: "str_from_utf8".into(),
            args: vec![format!("&{invalid_str}")],
        },
        Expected::FnRet {
            name: "str_from_utf8".into(),
            value: "core::result::Result::<&str, core::str::error::Utf8Error>::Err(core::str::error::Utf8Error {..})"
                .into(),
        },
        Expected::FnCall {
            name: "string_from_utf8".into(),
            args: vec![valid_str],
        },
        Expected::FnRet {
            name: "string_from_utf8".into(),
            value: "core::result::Result::<alloc::string::String, alloc::string::FromUtf8Error>::Ok(String::from(\"💖\"))"
                .into(),
        },
        Expected::FnCall {
            name: "string_from_utf8".into(),
            args: vec![invalid_str],
        },
        Expected::FnRet {
            name: "string_from_utf8".into(),
            value: "core::result::Result::<alloc::string::String, alloc::string::FromUtf8Error>::Err(alloc::string::FromUtf8Error { bytes: [..], error: core::str::error::Utf8Error { valid_up_to: 1usize, error_len: core::option::Option::<u8>::Some(1u8) } })"
                .into(),
        },
        Expected::FnCall {
            name: "result_boxed".into(),
            args: vec!["0i32".into()],
        },
        Expected::FnRet {
            name: "result_boxed".into(),
            value: result_boxed_ok.clone(),
        },
        Expected::FnCall {
            name: "passthru".into(),
            args: vec![result_boxed_ok.clone()],
        },
        Expected::FnRet {
            name: "passthru".into(),
            value: result_boxed_ok.clone(),
        },
        Expected::FnCall {
            name: "result_boxed".into(),
            args: vec!["1i32".into()],
        },
        Expected::FnRet {
            name: "result_boxed".into(),
            value: result_boxed_err.clone(),
        },
        Expected::FnCall {
            name: "passthru".into(),
            args: vec![result_boxed_err.clone()],
        },
        Expected::FnRet {
            name: "passthru".into(),
            value: result_boxed_err.clone(),
        },
        Expected::FnCall {
            name: "passthru".into(),
            args: vec![result_chain.clone()],
        },
        Expected::FnRet {
            name: "passthru".into(),
            value: result_chain.clone(),
        },
        Expected::FnRet {
            name: "main".into(),
            value: "()".into(),
        },
    ];

    let mut events = Vec::new();
    for i in 0..expected.len() {
        let payload = consumer.next().await?.message().into_bytes();
        let event = EventStream::read_from(Bytes::from(payload));
        println!("#{i} {:?}", event);
        events.push(event);
    }

    verify(testcase, events, expected);

    Ok(())
}

fn expand(string: &str) -> String {
    string
        .replace("RNode", "more_result::RNode")
        .replace("Result<", "core::result::Result::<")
        .replace("Box", "alloc::boxed::Box")
        .replace("Rc", "alloc::rc::Rc")
        .replace("Arc", "alloc::sync::Arc")
}