Skip to main content

reovim_testing/
lib.rs

1#![cfg_attr(coverage_nightly, allow(unused_features))]
2#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
3//! Integration test harness for reovim.
4//!
5//! This crate provides the **mechanism** layer for integration testing.
6//! Modules use this harness to define their test **policy** (what to test).
7//!
8//! # Architecture (Mechanism vs Policy)
9//!
10//! ```text
11//! shared/testing/            ← MECHANISM (this crate)
12//! ├── harness.rs             - Server process lifecycle
13//! ├── integration.rs         - Single-client test builder
14//! ├── multi_client.rs        - Multi-client test builder
15//! ├── step_test.rs           - Per-key state tracking
16//! └── assertions.rs          - Assertion macros
17//!
18//! server/modules/vim/tests/  ← POLICY (module-specific tests)
19//! ├── operators.rs        - What operators to test
20//! ├── registers.rs        - What register behaviors to verify
21//! └── cursor_movement.rs  - What cursor behaviors to verify
22//! ```
23//!
24//! # Protocol
25//!
26//! This crate uses **gRPC v2** for communicating with test servers.
27//! The legacy JSON-RPC v1 protocol is no longer supported.
28//!
29//! # Example Usage (in module tests)
30//!
31//! ```ignore
32//! use reovim_testing::{IntegrationTest, TestResult};
33//!
34//! #[tokio::test]
35//! async fn test_dd_deletes_line() {
36//!     let result = IntegrationTest::new()
37//!         .await
38//!         .with_buffer("hello\nworld")
39//!         .send_keys("dd")
40//!         .run()
41//!         .await;
42//!     result.assert_buffer_eq("world");
43//! }
44//! ```
45
46// Allow unused code - each consumer uses different subsets
47#![allow(dead_code)]
48
49mod assertions;
50pub mod frame;
51mod harness;
52mod integration;
53mod multi_client;
54mod presence;
55mod step_test;
56
57pub use {
58    harness::TestServerHarness,
59    integration::{IntegrationTest, RegisterInfo, TestResult},
60    multi_client::{MultiClientTest, TestClient},
61    presence::{MultiClientPresenceTest, PresenceTestClient},
62    step_test::{StateSnapshot, StepTest, StepTrace},
63};
64
65// Macros are automatically exported at crate level via #[macro_export]
66
67// ============================================================================
68// Demo Module Helpers (for module_loading.rs tests)
69// ============================================================================
70
71use std::path::PathBuf;
72
73/// Get the path to the demo module shared library.
74///
75/// Searches in order:
76/// 1. XDG data dir (`~/.local/share/reovim/modules/`) - installed modules with FFI symbols
77/// 2. `target/release/` - release build with FFI symbols
78/// 3. `target/debug/` - debug build (fallback, may not have FFI symbols)
79///
80/// # Returns
81///
82/// Path to `libreovim_module_hot_reload_demo.so` (or `.dylib` on macOS,
83/// `.dll` on Windows).
84///
85/// # Panics
86///
87/// Panics if the parent directory of `CARGO_MANIFEST_DIR` doesn't exist.
88#[must_use]
89#[cfg_attr(coverage_nightly, coverage(off))]
90pub fn demo_module_path() -> PathBuf {
91    let lib_name = demo_module_filename();
92
93    // First, check XDG data dir for installed modules (these have FFI symbols)
94    // XDG_DATA_HOME defaults to ~/.local/share
95    let xdg_data_home = std::env::var("XDG_DATA_HOME").map_or_else(
96        |_| {
97            std::env::var("HOME")
98                .map_or_else(|_| PathBuf::new(), |h| PathBuf::from(h).join(".local/share"))
99        },
100        PathBuf::from,
101    );
102    let xdg_path = xdg_data_home.join("reovim/modules").join(lib_name);
103
104    if xdg_path.exists() {
105        return xdg_path;
106    }
107
108    // Second, check release build (built with --features dynamic)
109    let manifest_dir = env!("CARGO_MANIFEST_DIR");
110    let release_path = PathBuf::from(manifest_dir)
111        .parent()
112        .expect("lib/testing should have parent directory")
113        .parent()
114        .expect("lib should have parent directory")
115        .join("target/release")
116        .join(lib_name);
117
118    if release_path.exists() {
119        return release_path;
120    }
121
122    // Fallback to debug build (may not have FFI symbols without --features dynamic)
123    PathBuf::from(manifest_dir)
124        .parent()
125        .expect("lib/testing should have parent directory")
126        .parent()
127        .expect("lib should have parent directory")
128        .join("target/debug")
129        .join(lib_name)
130}
131
132/// Get the filename for the demo module library.
133///
134/// Platform-specific:
135/// - Linux: `libreovim_module_hot_reload_demo.so`
136/// - macOS: `libreovim_module_hot_reload_demo.dylib`
137/// - Windows: `reovim_module_hot_reload_demo.dll`
138#[must_use]
139#[cfg_attr(coverage_nightly, coverage(off))]
140pub const fn demo_module_filename() -> &'static str {
141    #[cfg(target_os = "linux")]
142    {
143        "libreovim_module_hot_reload_demo.so"
144    }
145    #[cfg(target_os = "macos")]
146    {
147        "libreovim_module_hot_reload_demo.dylib"
148    }
149    #[cfg(target_os = "windows")]
150    {
151        "reovim_module_hot_reload_demo.dll"
152    }
153}