// Note that the `libbpf_set_print` function underlying `set_print()`
// and `ObjectBuilder::debug()` sets global state. To prevent issues
// with tests loading objects and similar we run print tests in separate
// processes.

use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;

use libbpf_rs::get_print;
use libbpf_rs::set_print;
use libbpf_rs::ObjectBuilder;
use libbpf_rs::PrintCallback;
use libbpf_rs::PrintLevel;

use test_fork::fork;


#[fork]
#[test]
fn test_set_print() {
    static CORRECT_LEVEL: AtomicBool = AtomicBool::new(false);
    static CORRECT_MESSAGE: AtomicBool = AtomicBool::new(false);

    fn callback(level: PrintLevel, msg: String) {
        if level == PrintLevel::Warn {
            CORRECT_LEVEL.store(true, Ordering::Relaxed);
        }

        if msg.starts_with("libbpf: ") {
            CORRECT_MESSAGE.store(true, Ordering::Relaxed);
        }
    }

    set_print(Some((PrintLevel::Debug, callback)));
    // expect_err requires that OpenObject implement Debug, which it does not.
    let obj = ObjectBuilder::default().open_file("/dev/null");
    assert!(obj.is_err(), "Successfully loaded /dev/null?");

    let correct_level = CORRECT_LEVEL.load(Ordering::Relaxed);
    let correct_message = CORRECT_MESSAGE.load(Ordering::Relaxed);
    assert!(correct_level, "Did not capture a warning");
    assert!(correct_message, "Did not capture the correct message");
}

#[fork]
#[test]
fn test_set_restore_print() {
    fn callback1(_: PrintLevel, _: String) {
        println!("one");
    }
    fn callback2(_: PrintLevel, _: String) {
        println!("two");
    }

    set_print(Some((PrintLevel::Warn, callback1)));
    let prev = get_print();
    assert_eq!(prev, Some((PrintLevel::Warn, callback1 as PrintCallback)));

    set_print(Some((PrintLevel::Debug, callback2)));
    let prev = get_print();
    assert_eq!(prev, Some((PrintLevel::Debug, callback2 as PrintCallback)));
}

#[fork]
#[test]
fn test_set_and_save_print() {
    fn callback1(_: PrintLevel, _: String) {
        println!("one");
    }
    fn callback2(_: PrintLevel, _: String) {
        println!("two");
    }

    set_print(Some((PrintLevel::Warn, callback1)));
    let prev = set_print(Some((PrintLevel::Debug, callback2)));
    assert_eq!(prev, Some((PrintLevel::Warn, callback1 as PrintCallback)));

    let prev = set_print(None);
    assert_eq!(prev, Some((PrintLevel::Debug, callback2 as PrintCallback)));
}
