diff --git a/src/config.rs b/src/config.rs index 5f46037b..c16a0f1a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -17,6 +17,8 @@ pub struct Config { #[serde(default)] pub hooks: HooksConfig, #[serde(default)] + pub limits: LimitsConfig, + #[serde(default)] pub gradle: GradleConfig, } @@ -96,6 +98,37 @@ impl Default for TelemetryConfig { } } +#[derive(Debug, Serialize, Deserialize)] +pub struct LimitsConfig { + /// Max total grep results to show (default: 200) + pub grep_max_results: usize, + /// Max matches per file in grep output (default: 25) + pub grep_max_per_file: usize, + /// Max staged/modified files shown in git status (default: 15) + pub status_max_files: usize, + /// Max untracked files shown in git status (default: 10) + pub status_max_untracked: usize, + /// Max chars for parser passthrough fallback (default: 2000) + pub passthrough_max_chars: usize, +} + +impl Default for LimitsConfig { + fn default() -> Self { + Self { + grep_max_results: 200, + grep_max_per_file: 25, + status_max_files: 15, + status_max_untracked: 10, + passthrough_max_chars: 2000, + } + } +} + +/// Get limits config. Falls back to defaults if config can't be loaded. +pub fn limits() -> LimitsConfig { + Config::load().map(|c| c.limits).unwrap_or_default() +} + #[derive(Debug, Serialize, Deserialize, Default)] pub struct GradleConfig { /// Package prefixes treated as "user code" in stack traces (kept, not dropped). diff --git a/src/gradle/batch.rs b/src/gradle/batch.rs index eecbbba4..2a470ba6 100644 --- a/src/gradle/batch.rs +++ b/src/gradle/batch.rs @@ -191,11 +191,7 @@ fn detect_task_type_from_name(task_name: &str) -> TaskType { fn filter_section_content(content: &str, task_type: &TaskType, task_name: &str) -> String { match task_type { TaskType::Compile => compile::filter_compile(content), - TaskType::Test => { - let name = task_name.rsplit(':').next().unwrap_or(task_name); - let is_integration = test_filter::is_integration_task_name(name); - test_filter::filter_test(content, is_integration) - } + TaskType::Test => test_filter::filter_test(content), TaskType::Detekt => detekt::filter_detekt(content), _ => content.to_string(), } diff --git a/src/gradle/mod.rs b/src/gradle/mod.rs index 64d081cf..a441ffc3 100644 --- a/src/gradle/mod.rs +++ b/src/gradle/mod.rs @@ -111,17 +111,6 @@ pub fn detect_task_type_from_output(raw: &str) -> TaskType { } } -/// Returns true if any of the given args refer to an integration/component/instrumented test task. -pub fn is_integration_test(args: &[String]) -> bool { - args.iter().any(|arg| { - let task_name = match arg.rfind(':') { - Some(pos) => arg[pos + 1..].to_ascii_lowercase(), - None => arg.to_ascii_lowercase(), - }; - test_filter::is_integration_task_name(&task_name) - }) -} - /// Find the gradle executable: prefer ./gradlew walking up parent dirs, fall back to gradle on PATH. fn find_gradle_executable() -> String { let candidates = [ @@ -240,8 +229,7 @@ pub fn run(args: &[String], verbose: u8) -> Result<()> { if task_type == TaskType::Generic { task_type = detect_task_type_from_output(&raw); } - let is_integration = is_integration_test(args); - let filtered = filter_gradle_output(&raw, &task_type, is_integration); + let filtered = filter_gradle_output(&raw, &task_type); let exit_code = output .status @@ -272,7 +260,7 @@ pub fn run(args: &[String], verbose: u8) -> Result<()> { } /// Apply task-type-specific filtering to gradle output. -pub fn filter_gradle_output(raw: &str, task_type: &TaskType, is_integration: bool) -> String { +pub fn filter_gradle_output(raw: &str, task_type: &TaskType) -> String { // For batch runs (multiple executed tasks), use batch filter on raw input // regardless of detected task type — batch filter splits by task boundaries // and applies per-section filters, preserving per-task context. @@ -285,7 +273,7 @@ pub fn filter_gradle_output(raw: &str, task_type: &TaskType, is_integration: boo match task_type { TaskType::Compile => compile::filter_compile(&filtered), - TaskType::Test => test_filter::filter_test(&filtered, is_integration), + TaskType::Test => test_filter::filter_test(&filtered), TaskType::Detekt => detekt::filter_detekt(&filtered), TaskType::Health => health::filter_health(&filtered), TaskType::Proto => proto::filter_proto(&filtered), @@ -589,30 +577,6 @@ mod tests { assert_eq!(detect_task_type_from_output(output), TaskType::Generic); } - // --- is_integration_test tests --- - - #[test] - fn test_is_integration_test_positive() { - let args = vec![":app:billing:integrationTest".to_string()]; - assert!(is_integration_test(&args)); - } - - #[test] - fn test_is_integration_test_negative() { - let args = vec![":app:billing:test".to_string()]; - assert!(!is_integration_test(&args)); - } - - #[test] - fn test_is_integration_test_mixed_args() { - let args = vec![ - "--continue".to_string(), - ":app:billing:integrationTest".to_string(), - "--info".to_string(), - ]; - assert!(is_integration_test(&args)); - } - // --- case-insensitive matching tests --- #[test] @@ -651,12 +615,6 @@ mod tests { assert_eq!(detect_task_type(&args), TaskType::Proto); } - #[test] - fn test_is_integration_test_case_insensitive() { - let args = vec![":app:billing:IntegrationTest".to_string()]; - assert!(is_integration_test(&args)); - } - // --- stderr noise filtering tests --- #[test] diff --git a/src/gradle/test_filter.rs b/src/gradle/test_filter.rs index 7236a12c..bc405079 100644 --- a/src/gradle/test_filter.rs +++ b/src/gradle/test_filter.rs @@ -18,17 +18,6 @@ pub fn matches_task(task_name: &str) -> bool { || t.starts_with("connected") } -/// Returns true if the task name specifically refers to an integration/component/instrumented test. -/// Used to enable integration-specific noise filtering (Hibernate, Spring, etc.) -/// Case-insensitive: callers may pass lowercase (CLI args) or original casing. -pub fn is_integration_task_name(task_name: &str) -> bool { - let t = task_name.to_ascii_lowercase(); - t == "integrationtest" - || t == "componenttest" - || t.contains("androidtest") - || t.starts_with("connected") -} - /// Built-in framework prefixes that are always dropped from stack traces. /// These are JDK/Kotlin stdlib and internal packages — universally noise. const BUILTIN_FRAMEWORK_PREFIXES: &[&str] = &[ @@ -91,7 +80,7 @@ fn build_framework_regex(drop_frame_packages: &[String]) -> Regex { } /// Apply TEST-specific filtering on top of globally-filtered output. -pub fn filter_test(input: &str, _is_integration: bool) -> String { +pub fn filter_test(input: &str) -> String { let (user_packages, drop_frame_packages) = load_config(); filter_test_with_config(input, &user_packages, &drop_frame_packages) } @@ -108,11 +97,7 @@ fn load_config() -> (Vec, Vec) { } /// Core test filter logic, testable with explicit config. -pub fn filter_test_with_packages( - input: &str, - _is_integration: bool, - user_packages: &[String], -) -> String { +pub fn filter_test_with_packages(input: &str, user_packages: &[String]) -> String { let drop_frame_packages = crate::config::default_drop_frame_packages(); filter_test_with_config(input, user_packages, &drop_frame_packages) } @@ -399,21 +384,6 @@ mod tests { assert!(matches_task("connectedAndroidTest")); } - // --- is_integration_task_name tests --- - - #[test] - fn test_integration_task_name_positive() { - assert!(is_integration_task_name("integrationTest")); - assert!(is_integration_task_name("componentTest")); - assert!(is_integration_task_name("connectedDebugAndroidTest")); - } - - #[test] - fn test_integration_task_name_negative() { - assert!(!is_integration_task_name("test")); - assert!(!is_integration_task_name("testDebugUnitTest")); - } - // --- build_framework_regex tests --- #[test] @@ -463,7 +433,7 @@ mod tests { fn test_test_success_snapshot() { let input = include_str!("../../tests/fixtures/gradle/test_success_raw.txt"); let globally_filtered = apply_global_filters(input); - let output = filter_test(&globally_filtered, false); + let output = filter_test(&globally_filtered); assert_snapshot!(output); } @@ -471,7 +441,7 @@ mod tests { fn test_test_failure_snapshot() { let input = include_str!("../../tests/fixtures/gradle/test_failure_raw.txt"); let globally_filtered = apply_global_filters(input); - let output = filter_test(&globally_filtered, false); + let output = filter_test(&globally_filtered); assert_snapshot!(output); } @@ -479,8 +449,7 @@ mod tests { fn test_test_failure_with_user_packages_snapshot() { let input = include_str!("../../tests/fixtures/gradle/test_failure_raw.txt"); let globally_filtered = apply_global_filters(input); - let output = - filter_test_with_packages(&globally_filtered, false, &["com.example".to_string()]); + let output = filter_test_with_packages(&globally_filtered, &["com.example".to_string()]); assert_snapshot!(output); } @@ -488,7 +457,7 @@ mod tests { fn test_integration_test_failure_snapshot() { let input = include_str!("../../tests/fixtures/gradle/integration_test_failure_raw.txt"); let globally_filtered = apply_global_filters(input); - let output = filter_test(&globally_filtered, true); + let output = filter_test(&globally_filtered); assert_snapshot!(output); } @@ -496,7 +465,7 @@ mod tests { fn test_test_failure_token_savings() { let input = include_str!("../../tests/fixtures/gradle/test_failure_raw.txt"); let globally_filtered = apply_global_filters(input); - let output = filter_test(&globally_filtered, false); + let output = filter_test(&globally_filtered); let input_tokens = count_tokens(input); let output_tokens = count_tokens(&output); let savings = 100.0 - (output_tokens as f64 / input_tokens as f64 * 100.0); @@ -513,7 +482,7 @@ mod tests { fn test_passing_tests_dropped() { let input = include_str!("../../tests/fixtures/gradle/test_failure_raw.txt"); let globally_filtered = apply_global_filters(input); - let output = filter_test(&globally_filtered, false); + let output = filter_test(&globally_filtered); assert!( !output.contains("PASSED"), "Passing test lines should be dropped" @@ -524,7 +493,7 @@ mod tests { fn test_failures_preserved() { let input = include_str!("../../tests/fixtures/gradle/test_failure_raw.txt"); let globally_filtered = apply_global_filters(input); - let output = filter_test(&globally_filtered, false); + let output = filter_test(&globally_filtered); assert!(output.contains("testChargeAmount FAILED")); assert!(output.contains("testRefundProcess FAILED")); assert!(output.contains("testCreateOrder FAILED")); @@ -643,7 +612,7 @@ mod tests { fn test_standard_out_dropped() { let input = include_str!("../../tests/fixtures/gradle/test_failure_raw.txt"); let globally_filtered = apply_global_filters(input); - let output = filter_test(&globally_filtered, false); + let output = filter_test(&globally_filtered); assert!( !output.contains("STANDARD_OUT"), "STANDARD_OUT header should be dropped"