Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,4 @@ owo-colors = { version = "4", features = ["supports-colors"] }

# Dev/test dependencies
temp-env = "0.3"
paste = "1"
1 change: 1 addition & 0 deletions crates/kild-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ hex = { workspace = true }

[dev-dependencies]
temp-env.workspace = true
paste.workspace = true

[target.'cfg(target_os = "macos")'.dependencies]
xcap.workspace = true
Expand Down
100 changes: 53 additions & 47 deletions crates/kild-core/src/agents/backends/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,45 @@
//! Agent backend implementations.
//!
//! All backends are defined via the `define_agent_backend!` macro, which generates
//! the struct, `AgentBackend` trait impl, and tests from a declarative one-liner.
//! the struct, `AgentBackend` trait impl, and uniquely named tests. Each invocation
//! requires a `test_prefix` identifier used to produce descriptive test function
//! names via `paste`.

/// Shared test body for both macro arms. Generates the four tests common to all
/// backends; the yolo-specific test is added by each arm individually.
#[cfg(test)]
macro_rules! define_agent_backend_tests {
($struct_name:ident, $prefix:ident, $name:expr, $display:expr, $cmd:expr, [$($pat:expr),+]) => {
paste::paste! {
#[test]
fn [<$prefix _backend_returns_correct_name>]() {
assert_eq!($struct_name.name(), $name);
}

#[test]
fn [<$prefix _backend_returns_correct_display_name>]() {
assert_eq!($struct_name.display_name(), $display);
}

#[test]
fn [<$prefix _backend_returns_correct_default_command>]() {
assert_eq!($struct_name.default_command(), $cmd);
}

#[test]
fn [<$prefix _backend_returns_expected_process_patterns>]() {
let patterns = $struct_name.process_patterns();
$(assert!(patterns.contains(&$pat.to_string()));)+
}
}
};
}

macro_rules! define_agent_backend {
// Arm with yolo_flags (agents that support autonomous mode)
(
$struct_name:ident,
test_prefix: $prefix:ident,
name: $name:expr,
display_name: $display:expr,
binary: $binary:expr,
Expand Down Expand Up @@ -47,36 +80,20 @@ macro_rules! define_agent_backend {
use super::*;
use crate::agents::traits::AgentBackend;

#[test]
fn test_name() {
assert_eq!($struct_name.name(), $name);
}
define_agent_backend_tests!($struct_name, $prefix, $name, $display, $cmd, [$($pat),+]);

#[test]
fn test_display_name() {
assert_eq!($struct_name.display_name(), $display);
}

#[test]
fn test_default_command() {
assert_eq!($struct_name.default_command(), $cmd);
}

#[test]
fn test_process_patterns() {
let patterns = $struct_name.process_patterns();
$(assert!(patterns.contains(&$pat.to_string()));)+
}

#[test]
fn test_yolo_flags() {
assert_eq!($struct_name.yolo_flags(), Some($yolo));
paste::paste! {
#[test]
fn [<$prefix _backend_returns_correct_yolo_flags>]() {
assert_eq!($struct_name.yolo_flags(), Some($yolo));
}
}
}
};
// Arm without yolo_flags (agents that don't support autonomous mode)
(
$struct_name:ident,
test_prefix: $prefix:ident,
name: $name:expr,
display_name: $display:expr,
binary: $binary:expr,
Expand Down Expand Up @@ -112,37 +129,21 @@ macro_rules! define_agent_backend {
use super::*;
use crate::agents::traits::AgentBackend;

#[test]
fn test_name() {
assert_eq!($struct_name.name(), $name);
}

#[test]
fn test_display_name() {
assert_eq!($struct_name.display_name(), $display);
}

#[test]
fn test_default_command() {
assert_eq!($struct_name.default_command(), $cmd);
}
define_agent_backend_tests!($struct_name, $prefix, $name, $display, $cmd, [$($pat),+]);

#[test]
fn test_process_patterns() {
let patterns = $struct_name.process_patterns();
$(assert!(patterns.contains(&$pat.to_string()));)+
}

#[test]
fn test_yolo_flags() {
assert_eq!($struct_name.yolo_flags(), None);
paste::paste! {
#[test]
fn [<$prefix _backend_returns_no_yolo_flags>]() {
assert_eq!($struct_name.yolo_flags(), None);
}
}
}
};
}

mod amp {
define_agent_backend!(AmpBackend,
test_prefix: amp,
name: "amp",
display_name: "Amp",
binary: "amp",
Expand All @@ -154,6 +155,7 @@ mod amp {

mod claude {
define_agent_backend!(ClaudeBackend,
test_prefix: claude,
name: "claude",
display_name: "Claude Code",
binary: "claude",
Expand All @@ -165,6 +167,7 @@ mod claude {

mod codex {
define_agent_backend!(CodexBackend,
test_prefix: codex,
name: "codex",
display_name: "Codex CLI",
binary: "codex",
Expand All @@ -176,6 +179,7 @@ mod codex {

mod gemini {
define_agent_backend!(GeminiBackend,
test_prefix: gemini,
name: "gemini",
display_name: "Gemini CLI",
binary: "gemini",
Expand All @@ -187,6 +191,7 @@ mod gemini {

mod kiro {
define_agent_backend!(KiroBackend,
test_prefix: kiro,
name: "kiro",
display_name: "Kiro CLI",
binary: "kiro-cli",
Expand All @@ -198,6 +203,7 @@ mod kiro {

mod opencode {
define_agent_backend!(OpenCodeBackend,
test_prefix: opencode,
name: "opencode",
display_name: "OpenCode",
binary: "opencode",
Expand Down
2 changes: 1 addition & 1 deletion crates/kild-core/src/agents/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ mod tests {
}

#[test]
fn test_agent_backend_basic_methods() {
fn mock_agent_backend_delegates_name_display_and_yolo_correctly() {
let backend = MockBackend;
assert_eq!(backend.name(), "mock");
assert_eq!(backend.display_name(), "Mock Agent");
Expand Down
4 changes: 2 additions & 2 deletions crates/kild-core/src/editor/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ mod tests {
}

#[test]
fn test_editor_backend_basic_methods() {
fn mock_editor_backend_is_available_and_not_a_terminal_editor() {
let backend = MockBackend;
assert_eq!(backend.name(), "mock");
assert_eq!(backend.display_name(), "Mock Editor");
Expand All @@ -90,7 +90,7 @@ mod tests {
}

#[test]
fn test_editor_backend_open() {
fn mock_editor_backend_open_succeeds() {
let backend = MockBackend;
let config = KildConfig::default();
let result = backend.open(Path::new("/tmp"), &[], &config);
Expand Down
8 changes: 4 additions & 4 deletions crates/kild-core/src/terminal/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,15 @@ mod tests {
}

#[test]
fn test_terminal_backend_basic_methods() {
fn mock_terminal_backend_name_and_availability_are_accessible() {
let backend = MockBackend;
assert_eq!(backend.name(), "mock");
assert_eq!(backend.display_name(), "Mock Terminal");
assert!(backend.is_available());
}

#[test]
fn test_terminal_backend_execute_spawn() {
fn mock_terminal_backend_execute_spawn_returns_window_title() {
let backend = MockBackend;
let config = SpawnConfig::new(
crate::terminal::types::TerminalType::Native,
Expand All @@ -142,9 +142,9 @@ mod tests {
}

#[test]
fn test_terminal_backend_close_window() {
fn mock_terminal_backend_close_window_does_not_panic() {
let backend = MockBackend;
// close_window returns () - just verify it doesn't panic
// Verify fire-and-forget close does not panic
backend.close_window(Some("123"));
}
}