neovm-core 0.0.2

Core runtime structures for NeoVM
mod common;

use common::{oracle_enabled, run_neovm_eval, run_oracle_eval};

fn run_case(name: &str, form: &str) {
    if !oracle_enabled() {
        eprintln!(
            "skipping advice stack audit: set NEOVM_FORCE_ORACLE_PATH or place GNU Emacs mirror alongside the repo"
        );
        return;
    }

    let gnu = run_oracle_eval(form).expect("GNU Emacs evaluation");
    let neovm = run_neovm_eval(form).expect("NeoVM evaluation");
    assert_eq!(
        neovm, gnu,
        "advice stack semantics mismatch for {}:\nGNU: {}\nNeoVM: {}",
        name, gnu, neovm
    );
}

#[test]
fn compat_backtrace_frame_marks_funcall_interactively_matches_gnu_emacs() {
    run_case(
        "backtrace_frame_marks_funcall_interactively",
        r#"
(progn
  (defun compat--bt-marker-target ()
    (interactive)
    (nth 1 (backtrace-frame 1 'compat--bt-marker-target)))
  (unwind-protect
      (list
       (funcall-interactively 'compat--bt-marker-target)
       (call-interactively 'compat--bt-marker-target))
    (fmakunbound 'compat--bt-marker-target)))
"#,
    );
}

#[test]
fn compat_advised_called_interactively_p_matches_gnu_emacs() {
    run_case(
        "advised_called_interactively_p",
        r#"
(progn
  (defun compat--advice-ci-target ()
    (interactive)
    (list (called-interactively-p 'any)
          (called-interactively-p 'interactive)))
  (defun compat--advice-ci-around (orig &rest args)
    (apply orig args))
  (advice-add 'compat--advice-ci-target :around 'compat--advice-ci-around)
  (unwind-protect
      (list
       (funcall-interactively 'compat--advice-ci-target)
       (call-interactively 'compat--advice-ci-target))
    (advice-remove 'compat--advice-ci-target 'compat--advice-ci-around)
    (fmakunbound 'compat--advice-ci-around)
    (fmakunbound 'compat--advice-ci-target)))
"#,
    );
}

#[test]
fn compat_around_advice_stack_shape_matches_gnu_emacs() {
    run_case(
        "around_advice_stack_shape",
        r#"
(progn
  (defun compat--advice-stack-target ()
    (interactive)
    (list 'target
          (called-interactively-p 'any)
          (called-interactively-p 'interactive)
          (nth 1 (backtrace-frame 1 'compat--advice-stack-target))))
  (defun compat--advice-stack-around (orig &rest args)
    (list 'around-enter
          (called-interactively-p 'any)
          (called-interactively-p 'interactive)
          (nth 1 (backtrace-frame 1 'compat--advice-stack-around))
          (apply orig args)))
  (advice-add 'compat--advice-stack-target :around 'compat--advice-stack-around)
  (unwind-protect
      (list
       (funcall-interactively 'compat--advice-stack-target)
       (call-interactively 'compat--advice-stack-target))
    (advice-remove 'compat--advice-stack-target 'compat--advice-stack-around)
    (fmakunbound 'compat--advice-stack-around)
    (fmakunbound 'compat--advice-stack-target)))
"#,
    );
}

#[test]
fn compat_before_advice_stack_shape_matches_gnu_emacs() {
    run_case(
        "before_advice_stack_shape",
        r#"
(progn
  (defvar compat--advice-stack-before-result nil)
  (defun compat--advice-stack-target ()
    (interactive)
    (list 'target
          (called-interactively-p 'any)
          (called-interactively-p 'interactive)
          (nth 1 (backtrace-frame 1 'compat--advice-stack-target))))
  (defun compat--advice-stack-before (&rest _args)
    (setq compat--advice-stack-before-result
          (list 'before
                (called-interactively-p 'any)
                (called-interactively-p 'interactive)
                (nth 1 (backtrace-frame 1 'compat--advice-stack-before)))))
  (advice-add 'compat--advice-stack-target :before 'compat--advice-stack-before)
  (unwind-protect
      (list
       (list
        (funcall-interactively 'compat--advice-stack-target)
        compat--advice-stack-before-result)
       (progn
         (setq compat--advice-stack-before-result nil)
         (list
          (call-interactively 'compat--advice-stack-target)
          compat--advice-stack-before-result)))
    (advice-remove 'compat--advice-stack-target 'compat--advice-stack-before)
    (fmakunbound 'compat--advice-stack-before)
    (fmakunbound 'compat--advice-stack-target)
    (makunbound 'compat--advice-stack-before-result)))
"#,
    );
}