use super::*;
use crate::panic::RefUnwindSafe;

fn generate_fake_frames() -> Vec<BacktraceFrame> {
    vec![
        BacktraceFrame {
            frame: RawFrame::Fake,
            symbols: vec![BacktraceSymbol {
                name: Some(b"std::backtrace::Backtrace::create".to_vec()),
                filename: Some(BytesOrWide::Bytes(b"rust/backtrace.rs".to_vec())),
                lineno: Some(100),
                colno: None,
            }],
        },
        BacktraceFrame {
            frame: RawFrame::Fake,
            symbols: vec![BacktraceSymbol {
                name: Some(b"__rust_maybe_catch_panic".to_vec()),
                filename: None,
                lineno: None,
                colno: None,
            }],
        },
        BacktraceFrame {
            frame: RawFrame::Fake,
            symbols: vec![
                BacktraceSymbol {
                    name: Some(b"std::rt::lang_start_internal".to_vec()),
                    filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())),
                    lineno: Some(300),
                    colno: Some(5),
                },
                BacktraceSymbol {
                    name: Some(b"std::rt::lang_start".to_vec()),
                    filename: Some(BytesOrWide::Bytes(b"rust/rt.rs".to_vec())),
                    lineno: Some(400),
                    colno: None,
                },
            ],
        },
    ]
}

#[test]
fn test_debug() {
    let backtrace = Backtrace {
        inner: Inner::Captured(
            (Capture { actual_start: 1, frames: generate_fake_frames() }).into(),
        ),
    };

    #[rustfmt::skip]
    let expected = "Backtrace [\
    \n    { fn: \"__rust_maybe_catch_panic\" },\
    \n    { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },\
    \n    { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },\
    \n]";

    assert_eq!(format!("{backtrace:#?}"), expected);

    // Format the backtrace a second time, just to make sure lazily resolved state is stable
    assert_eq!(format!("{backtrace:#?}"), expected);
}

#[test]
fn test_frames() {
    let backtrace = Backtrace {
        inner: Inner::Captured(
            (Capture { actual_start: 1, frames: generate_fake_frames() }).into(),
        ),
    };

    let frames = backtrace.frames();

    #[rustfmt::skip]
    let expected = vec![
        "[
    { fn: \"std::backtrace::Backtrace::create\", file: \"rust/backtrace.rs\", line: 100 },
]",
        "[
    { fn: \"__rust_maybe_catch_panic\" },
]",
        "[
    { fn: \"std::rt::lang_start_internal\", file: \"rust/rt.rs\", line: 300 },
    { fn: \"std::rt::lang_start\", file: \"rust/rt.rs\", line: 400 },
]"
    ];

    let mut iter = frames.iter().zip(expected.iter());

    assert!(iter.all(|(f, e)| format!("{f:#?}") == *e));
}

#[test]
fn backtrace_unwind_safe() {
    fn assert_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {}
    assert_unwind_safe::<Backtrace>();
}
