Skip to content

Reproducer test for #335 ModuleGraphParser stderr-pollution#340

Closed
tinder-maxwellelliott wants to merge 1 commit into
masterfrom
claude/reproducer-issue-335
Closed

Reproducer test for #335 ModuleGraphParser stderr-pollution#340
tinder-maxwellelliott wants to merge 1 commit into
masterfrom
claude/reproducer-issue-335

Conversation

@tinder-maxwellelliott
Copy link
Copy Markdown
Collaborator

Summary

  • Adds an @Ignored reproducer test for #335 on ModuleGraphParser.parseModuleGraph.
  • The test feeds the parser an INFO: Checking for file changes...\n{json} payload (the exact shape an 18.0.x base graph could produce because of the old Redirect.CAPTURE + redirectErrorStream(true) combo in BazelModService) and asserts that the parser still extracts both modules.
  • Today the parser calls JsonParser.parseString() on the whole input, fails, and returns emptyMap(). Downstream, findChangedModules(emptyMap, fullMap) treats every module in the new graph as "added" and CalculateImpactedTargetsInteractor.queryTargetsDependingOnModules() spawns a serial bazel query rdeps(//..., @@<repo>//...) subprocess per match — thousands of them on a real workspace, taking hours.
  • The fix is to make parseModuleGraph tolerant of leading non-JSON noise (strip stderr prefixes or locate the first { to parse from). Once that lands, remove @Ignore from the test.

This is a test-only change. The reproducer is marked @org.junit.Ignore so CI stays green; the annotation also documents the issue it tracks.

Test plan

  • bazel build //cli:cli-test-lib succeeds.
  • bazel test //cli:ModuleGraphParserTest passes (the new test is @Ignored and skipped; existing tests still pass).
  • After the parser fix lands, remove @Ignore and re-run bazel test //cli:ModuleGraphParserTest — the reproducer should pass.

🤖 Generated with Claude Code

bazel-diff 18.0.x captured stderr alongside stdout when fetching the bzlmod
graph (BazelModService used Redirect.CAPTURE for both streams, which
internally calls redirectErrorStream(true)). That meant getModuleGraphJson()
could return a payload shaped like:

  "INFO: Checking for file changes...\n{...real JSON...}\n"

18.1.0 fixed the redirect (#330), but CI workflows that reuse a base graph
file produced by an older deployment now feed this stderr-prefixed string
back into parseModuleGraph(). The current parser calls
JsonParser.parseString() on the whole input and silently returns
emptyMap() when parsing fails. Downstream, findChangedModules(emptyMap,
fullMap) treats every module in the new graph as "added" and
CalculateImpactedTargetsInteractor.queryTargetsDependingOnModules() spawns
a serial `bazel query rdeps(//..., @@<repo>//...)` subprocess per match --
thousands of them on a real workspace, taking hours.

This adds a @ignore'd reproducer test that asserts parseModuleGraph
tolerates a leading stderr line. Drop the @ignore once the parser strips
non-JSON noise / locates the first '{' to parse from.

Refs #335

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tinder-maxwellelliott added a commit that referenced this pull request May 13, 2026
…endingOnModules

Issue #335 lists multiple potential fixes; my first PR (#340) covered
fix #1 (parser robustness). This PR adds a reproducer for fix #3: the
loose substring match in
CalculateImpactedTargetsInteractor.queryTargetsDependingOnModules.

The current code is:

  val moduleRepos = allTargets.keys
      .filter { it.startsWith("@@") && it.contains(moduleName) }
      .map { it.substring(2).substringBefore("//") }
      .toSet()

`it.contains(moduleName)` is a very loose match. A single changed
module like `aspect_bazel_lib` matches every canonical repo whose name
starts with that string -- e.g. `@@aspect_bazel_lib+`,
`@@aspect_bazel_lib++toolchains+bats_toolchains`, etc. Each match
becomes its own serial `bazel query rdeps(//..., @@<repo>//...)`
subprocess. On the workspace in #335 that fan-out produced ~5,000
subprocesses and took multiple hours.

New test class CalculateImpactedTargetsInteractorIssue335Test:
- Wires its own koin module with a mock BazelQueryService that captures
  every query expression.
- Simulates a workspace with one module (aspect_bazel_lib) plus two
  module-extension repos whose canonical names begin with the same
  apparent name.
- Asserts that resolving the changed module yields exactly ONE rdeps
  query, scoped to the actual changed repo.

@ignore'd until the match is tightened (match canonical repo key
exactly, or look up via `bazel mod dump_repo_mapping`). cli/BUILD wires
up the new kt_jvm_test target.

Refs #335

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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