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}