raz_core/
test_commands.rs

1//! Enhanced test command generation with precise targeting
2
3use crate::{
4    Command, CommandBuilder, CommandCategory, ProjectContext, RazResult, TestContext,
5    TestTargetType,
6};
7
8/// Enhanced test command generator
9pub struct TestCommandGenerator;
10
11impl TestCommandGenerator {
12    /// Generate enhanced test commands from test context
13    pub fn generate_commands(
14        _context: &ProjectContext,
15        test_context: Option<&TestContext>,
16    ) -> RazResult<Vec<Command>> {
17        let mut commands = Vec::new();
18
19        if let Some(test_ctx) = test_context {
20            // Generate precise test command
21            if let Some(precise_cmd) = Self::build_precise_cargo_test(_context, test_ctx)? {
22                commands.push(precise_cmd);
23            }
24
25            // Generate nextest variant if available
26            if let Some(nextest_cmd) = Self::build_precise_nextest(_context, test_ctx)? {
27                commands.push(nextest_cmd);
28            }
29        }
30
31        Ok(commands)
32    }
33
34    /// Build precise cargo test command targeting specific test function
35    fn build_precise_cargo_test(
36        _context: &ProjectContext,
37        test_ctx: &TestContext,
38    ) -> RazResult<Option<Command>> {
39        let Some(test_name) = &test_ctx.test_name else {
40            return Ok(None);
41        };
42
43        let mut args = vec!["test".to_string()];
44
45        // Add package specification
46        if let Some(ref package) = test_ctx.package_name {
47            args.push("--package".to_string());
48            args.push(package.clone());
49        }
50
51        // Add target specification
52        match &test_ctx.target_type {
53            TestTargetType::Lib => args.push("--lib".to_string()),
54            TestTargetType::Bin(name) => {
55                args.push("--bin".to_string());
56                args.push(name.clone());
57            }
58            TestTargetType::Test(name) => {
59                args.push("--test".to_string());
60                args.push(name.clone());
61            }
62            TestTargetType::Bench(name) => {
63                args.push("--bench".to_string());
64                args.push(name.clone());
65            }
66            TestTargetType::Example(name) => {
67                args.push("--example".to_string());
68                args.push(name.clone());
69            }
70        }
71
72        // Add features
73        if !test_ctx.features.is_empty() {
74            args.push("--features".to_string());
75            args.push(test_ctx.features.join(","));
76        }
77
78        // Add test filter separator
79        args.push("--".to_string());
80
81        // Add full test path
82        if let Some(full_path) = test_ctx.full_test_path() {
83            args.push(full_path);
84        } else {
85            args.push(test_name.clone());
86        }
87
88        // Add exact match and output flags as defaults
89        // These are placed after -- where they belong
90        args.push("--exact".to_string());
91        args.push("--show-output".to_string());
92
93        let label = format!("Run Test: {test_name}");
94        let description = format!("Run the '{test_name}' test function with precise targeting");
95
96        let mut builder = CommandBuilder::new("cargo-test-precise", "cargo")
97            .label(label)
98            .description(description)
99            .args(args)
100            .category(CommandCategory::Test)
101            .priority(100)
102            .tag("test")
103            .tag("precise")
104            .tag("current");
105
106        // Set working directory
107        if let Some(ref working_dir) = test_ctx.working_dir {
108            builder = builder.cwd(working_dir.clone());
109        }
110
111        Ok(Some(builder.build()))
112    }
113
114    /// Build precise nextest command targeting specific test function
115    fn build_precise_nextest(
116        _context: &ProjectContext,
117        test_ctx: &TestContext,
118    ) -> RazResult<Option<Command>> {
119        let Some(test_name) = &test_ctx.test_name else {
120            return Ok(None);
121        };
122
123        let mut args = vec!["nextest".to_string(), "run".to_string()];
124
125        // Add package specification
126        if let Some(ref package) = test_ctx.package_name {
127            args.push("-p".to_string());
128            args.push(package.clone());
129        }
130
131        // Add target specification
132        match &test_ctx.target_type {
133            TestTargetType::Lib => args.push("--lib".to_string()),
134            TestTargetType::Bin(name) => {
135                args.push("--bin".to_string());
136                args.push(name.clone());
137            }
138            TestTargetType::Test(name) => {
139                args.push("--test".to_string());
140                args.push(name.clone());
141            }
142            TestTargetType::Bench(name) => {
143                args.push("--bench".to_string());
144                args.push(name.clone());
145            }
146            TestTargetType::Example(name) => {
147                args.push("--example".to_string());
148                args.push(name.clone());
149            }
150        }
151
152        // Add features
153        if !test_ctx.features.is_empty() {
154            args.push("--features".to_string());
155            args.push(test_ctx.features.join(","));
156        }
157
158        // Use filterset expression for precise targeting
159        if let Some(full_path) = test_ctx.full_test_path() {
160            args.push("-E".to_string());
161            args.push(format!("test({full_path})"));
162        } else {
163            args.push("-E".to_string());
164            args.push(format!("test({test_name})"));
165        }
166
167        let label = format!("Run Test (nextest): {test_name}");
168        let description =
169            format!("Run the '{test_name}' test function with nextest and precise targeting");
170
171        let builder = CommandBuilder::new("nextest-test-precise", "cargo")
172            .label(label)
173            .description(description)
174            .args(args)
175            .category(CommandCategory::Test)
176            .priority(95)
177            .tag("test")
178            .tag("nextest")
179            .tag("precise")
180            .tag("current");
181
182        Ok(Some(builder.build()))
183    }
184}