Skip to content

feat(daemon): classify test vs production files per SonarQube convention#3

Merged
aksOps merged 2 commits into
mainfrom
feat/test-main-split
May 23, 2026
Merged

feat(daemon): classify test vs production files per SonarQube convention#3
aksOps merged 2 commits into
mainfrom
feat/test-main-split

Conversation

@aksOps

@aksOps aksOps commented May 23, 2026

Copy link
Copy Markdown
Contributor

Adds the test/main code split SonarQube proper supports. Each input file gets an isTest flag based on language-conventional paths; sonarlint-analysis-engine then skips Main-only rules on test files (java:S100 method naming, java:S106 System.out, java:S1118 utility-class ctor, ...). We override no rules — only classify files correctly so the analyzer's own scope metadata does its job.

Verified self-scan result

Metric Before After Delta
Total issues 263 77 −186 (−70%)
java:S100 (method naming) 70 0 all 70 were on test methods
java:S106 (System.out) 13 11 (all main) tests get a pass
java:S1118 (utility-class ctor) 1 1 (main) tests untouched

The 70 java:S100 hits the previous self-scan flagged on daemon/src/test/java/... test methods are gone — same code, same rules, same analyzer; the classifier just tells the engine what's a test.

Classification (industry-standard per language)

Language Patterns
Java / Kotlin src/test/**, **/*Test.java, **/*Tests.java, **/*IT.java (and .kt)
Scala src/test/**, **/*Test.scala
Go **/*_test.go
Python tests/**, test/**, **/test_*.py, **/*_test.py
JS / TS tests/**, __tests__/**, **/*.{test,spec}.{js,jsx,ts,tsx}
PHP tests/**, **/*Test.php
Ruby spec/**, test/**, **/*_spec.rb, **/*_test.rb
HTML / XML / CSS no convention → MAIN

Cross-language paths like src/test/, tests/, __tests__/, spec/ count regardless of language.

Agent / skill override

A new global CLI option --test-path GLOB (repeatable) augments the built-in detection for non-standard layouts:

sonar --test-path 'src/integration/**' --test-path 'e2e/**' analyze .

Plumbed through AnalyzeRequest.additionalTestPaths (new field, with a 5-arg backward-compat constructor so existing call sites keep compiling). Standalone runs without an agent fall back to the detector defaults.

Tested

  • 21-case unit test on TestPathDetector (cross-language paths, per-language filenames, negatives, Windows backslashes, null/empty edge cases) — all pass.
  • Full daemon test suite green.
  • Full reactor build (protocol, daemon, cli, dist) green.
  • End-to-end self-scan: 263 → 77 issues, 0 java:S100 (verified against a fresh daemon running this branch's code).

🤖 Generated with Claude Code

Adds the test/main code split SonarQube proper supports. Each input file
is now classified as test or main, and that classification flows to the
engine via ClientInputFile.isTest(). SonarSource rules carry a scope
(Main, Test, All); Main-only rules (java:S100 method naming, java:S106
System.out, java:S1118 utility-class ctor, ...) now correctly skip test
files. We are not overriding any rule — only classifying files correctly
so the analyzer's own scope metadata can do its job.

Classification rules (industry-standard per language):
  Java/Kotlin:  src/test/**, *Test.java, *Tests.java, *IT.java (and .kt)
  Scala:        src/test/**, *Test.scala
  Go:           *_test.go
  Python:       tests/**, test/**, test_*.py, *_test.py
  JS/TS:        tests/**, __tests__/**, *.{test,spec}.{js,jsx,ts,tsx}
  PHP:          tests/**, *Test.php
  Ruby:         spec/**, test/**, *_spec.rb, *_test.rb
  HTML/XML/CSS: no convention; classified as MAIN
Cross-language paths like src/test/, tests/, __tests__/, spec/ also count.

Files:
  daemon: new TestPathDetector + 21-case unit test
  daemon: FileInputFile gains an isTest field (4-arg ctor; old 3-arg
          ctor delegates with isTest=false for backward compatibility)
  daemon: AnalysisService classifies each file before constructing
          its FileInputFile
  protocol: AnalyzeRequest gains an additionalTestPaths field for
          agent/skill overrides; a backward-compat 5-arg constructor
          keeps all existing call sites compiling
  daemon: AnalysisService glob-matches additionalTestPaths against
          each file (additive to the built-in detector)
  cli:   global --test-path GLOB option (repeatable) plumbed into the
          AnalyzeRequest

Skills and agents that know a project's layout (e.g. integration tests
under src/integration/) pass --test-path 'src/integration/**'. Standalone
invocations without an agent fall back to the built-in detector.

Verified end to end (self-scan of this repo):

  Before          After
  ─────────       ─────────
  263 issues  ->   77 issues          (-186; -70%)
  java:S100:  70 ->   0               (all 70 were test method naming)
  java:S106:  13 ->  11 main, 0 test  (System.out — tests get a pass)
  java:S1118:  ? ->   1 main, 0 test  (utility-class ctor)

All 21 detector unit tests pass; full daemon test suite green; full
reactor build (protocol, daemon, cli, dist) green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@aksOps aksOps force-pushed the feat/test-main-split branch from 8d84b3c to 3e19e3e Compare May 23, 2026 07:34
Layers the test/main classification feature into the user-facing docs that
PR #3 left untouched:

- SKILL.md now states the daemon applies SonarSource's Main/Test rule scope
  (java:S100, S106, S1118, ... skip test files), lists the auto-detected
  language-conventional paths, and documents --test-path GLOB as the agent
  escape hatch for non-standard layouts.

- sonar-scanner-claude (haiku) and sonar-scanner-copilot (gpt-5-mini) both
  gain a 'Non-standard test layouts' section instructing the agent to
  inspect the project layout before scanning and pass --test-path globs
  for non-conventional directories. Worded to discourage over-classification
  — the goal is to not flag intentional test conventions, not to silence
  rules.

No code change in this commit; doc/skill updates only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@aksOps aksOps merged commit 060a2c2 into main May 23, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant