//! Ensure that `for_each_function!` isn't missing any symbols.

use std::collections::HashSet;
use std::env;
use std::path::Path;
use std::process::Command;

macro_rules! callback {
    (
        fn_name: $name:ident,
        attrs: [$($attr:meta),*],
        extra: [$set:ident],
    ) => {
        let name = stringify!($name);
        let new = $set.insert(name);
        assert!(new, "duplicate function `{name}` in `ALL_OPERATIONS`");
    };
}

#[test]
fn test_for_each_function_all_included() {
    let all_functions: HashSet<_> = include_str!("../../etc/function-list.txt")
        .lines()
        .filter(|line| !line.starts_with("#"))
        .collect();

    let mut tested = HashSet::new();

    libm_macros::for_each_function! {
        callback: callback,
        extra: [tested],
    };

    let untested = all_functions.difference(&tested);
    if untested.clone().next().is_some() {
        panic!(
            "missing tests for the following: {untested:#?} \
            \nmake sure any new functions are entered in \
            `ALL_OPERATIONS` (in `libm-macros`)."
        );
    }

    // FIXME: This needs to be updated to interact with compiler-builtins
    // assert_eq!(
    //     all_functions,
    //     tested,
    //     "difference: {:?}",
    //     tested.difference(&all_functions)
    // );
}

#[test]
fn ensure_list_updated() {
    if libm_test::ci() {
        // Most CI tests run in Docker where we don't have Python or Rustdoc, so it's easiest
        // to just run the python file directly when it is available.
        eprintln!("skipping test; CI runs the python file directly");
        return;
    }

    let res = Command::new("python3")
        .arg(Path::new(env!("CARGO_MANIFEST_DIR")).join("../etc/update-api-list.py"))
        .arg("--check")
        .status()
        .unwrap();

    assert!(res.success(), "May need to run `./etc/update-api-list.py`");
}
