Merge pull request #3140 from rtk-ai/fix/sbt-testonly-filter

fix(sbt): filter testOnly/testQuick like sbt test
This commit is contained in:
aesoft
2026-07-22 12:37:17 +02:00
committed by GitHub
3 changed files with 62 additions and 4 deletions
+31 -2
View File
@@ -60,6 +60,12 @@ fn is_integration_test_cmd(subcommand: &str) -> bool {
) || (subcommand.ends_with(":test") || subcommand.ends_with("/test"))
}
fn is_test_task(subcommand: &str) -> bool {
let task = subcommand.split_whitespace().next().unwrap_or(subcommand);
let task = task.rsplit(['/', ':']).next().unwrap_or(task);
matches!(task, "testOnly" | "testQuick")
}
/// Returns true if `s` is a scoped SBT task (e.g. `Test/test`, `it/Test/compile`).
fn is_scoped_task(s: &str) -> bool {
!s.starts_with('-') && (s.contains('/') || s.contains(':'))
@@ -130,7 +136,7 @@ pub fn run_other(args: &[OsString], verbose: u8) -> Result<i32> {
// ScalaTest output — filter them like `sbt test`, through the shared runner so the
// never_worse cap and tee hint apply (an unrecognized output would otherwise be
// reprinted verbatim plus the hint, i.e. more than the raw command produced).
if is_integration_test_cmd(&subcommand) {
if is_integration_test_cmd(&subcommand) || is_test_task(&subcommand) {
let mut cmd = resolved_command("sbt");
cmd.arg(&subcommand);
for arg in &args[1..] {
@@ -141,6 +147,12 @@ pub fn run_other(args: &[OsString], verbose: u8) -> Result<i32> {
eprintln!("Running: sbt {} ...", subcommand);
}
let tee_label = if is_integration_test_cmd(&subcommand) {
"sbt_it_test"
} else {
"sbt_test"
};
let rest: Vec<String> = args[1..]
.iter()
.map(|a| a.to_string_lossy().into_owned())
@@ -156,7 +168,7 @@ pub fn run_other(args: &[OsString], verbose: u8) -> Result<i32> {
"sbt",
&args_display,
filter_sbt_test,
RunOptions::with_tee("sbt_it_test"),
RunOptions::with_tee(tee_label),
);
}
@@ -694,6 +706,23 @@ mod tests {
assert!(!is_integration_test_cmd("assembly"));
}
#[test]
fn test_is_test_task() {
assert!(is_test_task("testOnly"));
assert!(is_test_task("testQuick"));
assert!(is_test_task("Test/testOnly"));
assert!(is_test_task("core/testOnly"));
assert!(is_test_task("it:testOnly"));
assert!(is_test_task("testOnly com.example.MySpec"));
assert!(is_test_task("testOnly *CalcSpec"));
assert!(is_test_task("core/testOnly com.example.MySpec"));
assert!(!is_test_task("test"));
assert!(!is_test_task("Test/test"));
assert!(!is_test_task("testOnlyFoo"));
assert!(!is_test_task("compile"));
assert!(!is_test_task("clean; testOnly com.example.MySpec"));
}
// --- sbt test: munit format ---
#[test]
+29
View File
@@ -3399,6 +3399,35 @@ mod tests {
);
}
#[test]
fn test_rewrite_sbt_test_only() {
assert_eq!(
rewrite_command_no_prefixes("sbt testOnly com.example.MySpec", &[]),
Some("rtk sbt testOnly com.example.MySpec".into())
);
assert_eq!(
rewrite_command_no_prefixes(r#"sbt "testOnly com.example.MySpec""#, &[]),
Some(r#"rtk sbt "testOnly com.example.MySpec""#.into())
);
assert_eq!(
rewrite_command_no_prefixes(r#"sbt "testOnly *MySpec -- -z foo""#, &[]),
Some(r#"rtk sbt "testOnly *MySpec -- -z foo""#.into())
);
assert_eq!(
rewrite_command_no_prefixes("sbt testQuick", &[]),
Some("rtk sbt testQuick".into())
);
}
#[test]
fn test_rewrite_sbt_does_not_match_unrelated_tasks() {
assert_eq!(rewrite_command_no_prefixes("sbt testify", &[]), None);
assert_eq!(
rewrite_command_no_prefixes(r#"sbt "test:compile""#, &[]),
None
);
}
// --- Maven ---
#[test]
+2 -2
View File
@@ -508,12 +508,12 @@ pub const RULES: &[RtkRule] = &[
},
// Scala/SBT
RtkRule {
pattern: r"^sbt\s+(test|compile|run|clean|assembly|package)(?:\s|$)",
pattern: r#"^sbt\s+["']?(testOnly|testQuick|test|compile|run|clean|assembly|package)(?:[\s"']|$)"#,
rtk_cmd: "rtk sbt",
rewrite_prefixes: &["sbt"],
category: "Build",
savings_pct: 80.0,
subcmd_savings: &[("test", 90.0), ("compile", 75.0)],
subcmd_savings: &[("test", 90.0), ("testOnly", 90.0), ("compile", 75.0)],
subcmd_status: &[],
},
RtkRule {