Skip to content

Fix duplicate generated sources in the test source set#78

Merged
alexander-yevsyukov merged 17 commits into
masterfrom
claude/vibrant-jang-fa76b9
Jul 1, 2026
Merged

Fix duplicate generated sources in the test source set#78
alexander-yevsyukov merged 17 commits into
masterfrom
claude/vibrant-jang-fa76b9

Conversation

@alexander-yevsyukov

@alexander-yevsyukov alexander-yevsyukov commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

What

Fixes #19 — generated source sets in tests may contain duplicates, causing
duplicate class compilation failures in the test source set.

protoc writes generated code to build/generated/sources/proto/<sourceSet>/…
and the Spine Compiler writes its processed output to generated/<sourceSet>/….
Both must not reach javac/kotlinc at once. The plugin deduplicates by
rewriting source-set directories once during configuration
(configureSourceSetDirs, in tool-base), but that eager rewrite is fragile and
does not always hold for the test source set — depending on the
plugin-application order, a symlinked project path, or another plugin re-adding
the directory, the protoc output may end up back in the source set.

Changes

  • Plugin.kt — new excludeProtocOutputFromCompilation() keeps the protoc
    output directory out of every JavaCompile/KotlinCompile source via a live
    filter, so the deduplication holds regardless of when the directory was added
    to the source set. This re-introduces the order-independent compile-task
    filtering the issue references (the historic configureCompileTasks, removed
    from ProtoData in 2023). JavaCompile re-sets its source to a filtered view
    (it ignores exclude(Spec) for the files it passes to javac); KotlinCompile
    honors exclude(Spec). The deprecated in-place mode is left untouched.
  • Paths.ktresidesIn now canonicalizes both operands, so a symlinked
    project path no longer defeats the check (a latent canonical-vs-absolute bug,
    a likely "sometimes" trigger).
  • PluginSpec.kt + test-source-set/ fixture — regression test that re-adds
    the protoc output directory to the test source set (reproducing the leaked
    state) and asserts the test source set still compiles.
  • version.gradle.kts2.0.0-SNAPSHOT.057.058.

Verification

  • Reproduced RED (compileTestJava fails with duplicate class: …TestScoped),
    GREEN with the fix.
  • Full PluginSpec: 16 tests, 0 failures (1 pre-existing skip).
  • :gradle-plugin:build + dokkaGenerate (all modules) pass.

Pre-PR reviewers

kotlin-engineer (APPROVE WITH CHANGES — clarifying comments applied),
spine-code-review (APPROVE), review-docs (APPROVE). No Must-fix items.

🤖 Generated with Claude Code

Additional changes — dependency / build-tooling update (folded in to unblock CI)

The first commit is the #19 fix. The later commits carry a dependency and
build-tooling update that is required to get this branch's CI green: the
repository's self-build was broken by an upstream snapshot,
core-jvm-compiler 2.0.0-SNAPSHOT.080, whose generated code for the compiler's
own Protobuf types was not loadable at test runtime — failing :backend:test
and the performance smoke test on every current PR. Resolving it required:

  • Spine SDK dependency bumps under buildSrc/src/main/kotlin/io/spine/dependency/**,
    with docs/dependencies/** regenerated to match.
  • Gradle wrapper 9.5.19.6.1.
  • Codecov GitHub Action v4v7 (.github/workflows/build-on-ubuntu.yml).
  • config submodule bumped.

These are intentional and make the self-build (and therefore this PR's checks)
pass on top of the #19 fix.

Copilot AI review requested due to automatic review settings June 30, 2026 19:11
@alexander-yevsyukov alexander-yevsyukov self-assigned this Jun 30, 2026
@alexander-yevsyukov alexander-yevsyukov moved this to 🏗 In progress in v2.0 Jun 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses issue #19 where protoc-generated sources can leak back into the test source set, causing duplicate-class compilation failures. It does so by making exclusion of the protoc output directory order-independent at compile-task level and by hardening path containment checks for symlinked project paths.

Changes:

  • Add compile-task-level filtering to keep protoc output out of JavaCompile/Kotlin compilation inputs regardless of source set mutation order.
  • Make File.residesIn() symlink-safe by canonicalizing both operands before comparison.
  • Add a functional-test fixture and regression test reproducing the leaked test source-set state and asserting compilation succeeds.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
version.gradle.kts Bumps compiler snapshot version.
gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Plugin.kt Adds live compile-task filtering to exclude protoc output from compilation inputs.
gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Paths.kt Updates residesIn() to use canonicalized operands for symlink robustness.
gradle-plugin/src/functionalTest/kotlin/io/spine/tools/compiler/gradle/plugin/PluginSpec.kt Adds regression test ensuring test compilation succeeds even if protoc output is re-added.
gradle-plugin/src/functionalTest/resources/test-source-set/build.gradle.kts Test fixture that re-adds protoc output dirs to test sources post-configuration to reproduce the failure mode.
gradle-plugin/src/functionalTest/resources/test-source-set/settings.gradle.kts Test fixture settings for plugin resolution via mavenLocal.
gradle-plugin/src/functionalTest/resources/test-source-set/src/main/proto/main_scope.proto Adds main-scope proto for the fixture.
gradle-plugin/src/functionalTest/resources/test-source-set/src/test/proto/test_scope.proto Adds test-scope proto for the fixture.
.agents/tasks/fix-test-source-set-duplicates.md Config-managed agent task file (not reviewed per repository guidelines).

Comment thread gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Paths.kt Outdated
@alexander-yevsyukov alexander-yevsyukov force-pushed the claude/vibrant-jang-fa76b9 branch 2 times, most recently from ef1a4d3 to 40a3ca3 Compare June 30, 2026 19:52
Copilot AI review requested due to automatic review settings June 30, 2026 19:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.

Comment thread gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Plugin.kt Outdated
`protoc` writes generated code to `build/generated/sources/proto/<sourceSet>`
and the Compiler writes its processed output to `generated/<sourceSet>`. Both
must not reach `javac`/`kotlinc` at once, or compilation fails with
`duplicate class` errors. The plugin deduplicates by rewriting source-set
directories once during configuration (`configureSourceSetDirs`), but that
eager rewrite is fragile and does not always hold for the `test` source set —
depending on the plugin-application order, a symlinked project path, or another
plugin re-adding the directory, the `protoc` output may end up back in the set.

Re-introduce order-independent compile-task filtering (the historic
`configureCompileTasks` mechanism): `excludeProtocOutputFromCompilation()` keeps
the `protoc` output directory out of every `JavaCompile`/`KotlinCompile` source
via a live filter, so the deduplication holds regardless of when the directory
was added. `JavaCompile` re-sets its `source` to a filtered view (it ignores
`exclude(Spec)` for the files it passes to `javac`); `KotlinCompile` honors
`exclude(Spec)`. The deprecated in-place mode is left untouched.

Harden `Paths.residesIn` to canonicalize both operands so a symlinked project
path no longer defeats the check.

Add the `test-source-set` functional-test fixture and a regression test that
reproduces the leaked state and asserts the `test` source set still compiles.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alexander-yevsyukov alexander-yevsyukov force-pushed the claude/vibrant-jang-fa76b9 branch from 40a3ca3 to 4199846 Compare June 30, 2026 19:58
Copilot AI review requested due to automatic review settings June 30, 2026 20:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.

Comment thread buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt Outdated
Comment thread gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Plugin.kt Outdated
`CoreJvmCompiler` 2.0.0-SNAPSHOT.080 breaks the repository's self-build: the
code it generates for the compiler's own Protobuf types is not loadable at test
runtime, so `:backend:test` and `:backend:performanceTest` fail with
`io.spine.type.UnknownTypeException: No Java class found for the Protobuf
message of type ...` for many types (e.g. `spine.compiler.ProtobufSourceFile`).
This affects every PR built against the current `master`.

Pin both `dogfoodingVersion` (build classpath) and `version` back to the
last-known-good `.079`, under which `:backend:test` (0 failed) and
`:backend:performanceTest` pass locally.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alexander-yevsyukov alexander-yevsyukov force-pushed the claude/vibrant-jang-fa76b9 branch from 7ebf4f8 to 6f754c9 Compare June 30, 2026 20:22
Copilot AI review requested due to automatic review settings June 30, 2026 20:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Comment thread gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Plugin.kt Outdated
Comment thread docs/dependencies/pom.xml
Comment thread docs/dependencies/pom.xml
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 65.21739% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 76.51%. Comparing base (e7028ab) to head (e41e7f1).
⚠️ Report is 18 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master      #78      +/-   ##
============================================
+ Coverage     75.62%   76.51%   +0.89%     
- Complexity      677      681       +4     
============================================
  Files           203      203              
  Lines          3950     3972      +22     
  Branches        392      396       +4     
============================================
+ Hits           2987     3039      +52     
+ Misses          845      807      -38     
- Partials        118      126       +8     
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

alexander-yevsyukov and others added 2 commits June 30, 2026 22:14
`residesIn` canonicalizes both operands, so calling it per source file
re-canonicalized the `protoc` output directory on every file. Add a
`residesIn(Path)` overload that takes an already-canonical directory, and
canonicalize the `protoc` output once in `excludeProtocOutputFromCompilation`,
avoiding the repeated filesystem work on large source sets.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`KotlinCompilationTask` comes from the Kotlin Gradle Plugin, which is a
`compileOnly` dependency and is therefore not on the plugin's runtime classpath.
Referencing it via a class literal forced the class to load even for consumers
that apply `io.spine.compiler` without Kotlin (e.g. `tests/in-place-consumer`),
risking a `NoClassDefFoundError` during plugin application. Guard the Kotlin
compile-task filtering with `hasKotlin()` (a name-based check), so the task type
is referenced only when Kotlin is present.

Also align the `compiler-cli-all` and `compiler-protoc-plugin` versions in
`docs/dependencies/pom.xml` with the bumped project version (`.058`), matching
`dependencies.md`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 30, 2026 21:43

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@alexander-yevsyukov alexander-yevsyukov moved this from 🏗 In progress to In Review in v2.0 Jun 30, 2026
alexander-yevsyukov and others added 3 commits June 30, 2026 23:07
Cover `excludeProtocOutputFromCompilation` and `File.residesIn` with in-process
tests so the deduplication logic is exercised under Kover. The functional test
runs in a separate TestKit JVM that coverage cannot instrument, which left the
new lines uncovered.

`ExcludeProtocOutputSpec` evaluates a project with the plugin applied and asserts
a file under the `protoc` output directory is kept out of the `compileJava`
source. `PathsSpec` covers the nested, self, sibling, and already-canonical cases
of `residesIn`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 30, 2026 22:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 41 changed files in this pull request and generated 1 comment.

Comment thread version.gradle.kts Outdated
@alexander-yevsyukov alexander-yevsyukov changed the title Fix duplicate generated sources in the test source set (#19) Fix duplicate generated sources in the test source set Jul 1, 2026
Copilot AI review requested due to automatic review settings July 1, 2026 14:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.

@alexander-yevsyukov alexander-yevsyukov merged commit 55c0289 into master Jul 1, 2026
10 checks passed
@alexander-yevsyukov alexander-yevsyukov deleted the claude/vibrant-jang-fa76b9 branch July 1, 2026 15:42
@github-project-automation github-project-automation Bot moved this from In Review to ✅ Done in v2.0 Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

Generated source sets in tests may contain duplicates

3 participants