;;; test-lsp.el --- Test Rust LSP diagnostics with Eglot -*- lexical-binding: t -*- ;;; run with: emacs -batch -l test-lsp.el ;;; Commentary: ;; This test verifies that Eglot (Emacs LSP client) correctly detects ;; type errors in Rust code using rust-analyzer. ;; ;; The test: ;; 1. Loads the user's Emacs configuration (~/.emacs.d) ;; 2. Opens src/main.rs (which contains an intentional type error) ;; 3. Starts Eglot and connects to rust-analyzer ;; 4. Waits for LSP diagnostics to be reported ;; 5. Verifies the expected type error is detected ;; ;; Exit codes: ;; - 0: Test passed (type error detected) ;; - 1: Test failed (no diagnostics or wrong error) ;;; Code: ;; Load user config (external dependency) (load "~/.emacs.d/early-init.el" nil t) (load "~/.emacs.d/init.el" nil t) (require 'eglot) (require 'flymake) (defconst test-lsp--expected-error-patterns '("mismatched types" "expected.*i32" "found.*&str" "E0308") "Regex patterns to identify the expected type error.") (defconst test-lsp--timeout-seconds 30 "Maximum time to wait for LSP diagnostics.") (defun test-lsp--check-diagnostic (diag) "Check if DIAG matches the expected type error patterns. Returns t if matched, nil otherwise." (let ((msg (flymake-diagnostic-text diag))) (cl-some (lambda (pattern) (string-match-p pattern msg)) test-lsp--expected-error-patterns))) (defun test-lsp--start-eglot () "Start Eglot and wait for connection. Returns the server object on success, signals error on failure." (message "Starting eglot...") (let ((eglot-autoshutdown t) (eglot-sync-connect 10) (current-prefix-arg nil)) (call-interactively 'eglot)) (let ((server (eglot-current-server))) (unless server (error "Eglot failed to start")) (message "Eglot connected: %s" (eglot--project-nickname server)) server)) (defun test-lsp--wait-for-diagnostics (timeout) "Wait up to TIMEOUT seconds for LSP diagnostics. Returns the list of diagnostics, or nil if timeout reached." (message "Waiting for diagnostics...") (let ((start-time (float-time)) diagnostics) (while (and (< (- (float-time) start-time) timeout) (progn (setq diagnostics (flymake-diagnostics (point-min) (point-max))) (or (null diagnostics) (= (length diagnostics) 0)))) (accept-process-output nil 0.1)) diagnostics)) (defun test-rust-lsp-diagnostics () "Open a Rust file, start Eglot, and verify type error detection. Exits with status 0 on success, 1 on failure." (let ((file "src/main.rs")) ;; Open the test file (find-file file) (message "Opened %s" (buffer-file-name)) ;; Verify we're in a Rust mode (unless (or (derived-mode-p 'rust-mode) (derived-mode-p 'rust-ts-mode)) (error "Not in a Rust mode (current: %s)" major-mode)) ;; Start Eglot (test-lsp--start-eglot) ;; Wait for diagnostics (let ((diagnostics (test-lsp--wait-for-diagnostics test-lsp--timeout-seconds))) ;; Check results (if (and diagnostics (> (length diagnostics) 0)) (let ((found-error nil)) (message "Found %d diagnostic(s)" (length diagnostics)) ;; Check all diagnostics for the type error (dolist (diag diagnostics) (let ((type (flymake-diagnostic-type diag)) (msg (flymake-diagnostic-text diag))) (message " - [%s] %s" type msg) (when (test-lsp--check-diagnostic diag) (setq found-error t)))) (if found-error (progn (message "TEST PASSED: Found expected type error") (kill-emacs 0)) (progn (message "TEST FAILED: Type error not found in diagnostics") (kill-emacs 1)))) (message "TEST FAILED: No diagnostics received within %d seconds" test-lsp--timeout-seconds) (kill-emacs 1))))) ;; Run the test (condition-case err (test-rust-lsp-diagnostics) (error (message "TEST ERROR: %s" (error-message-string err)) (kill-emacs 1))) ;;; test-lsp.el ends here