From d0b694d3edc741427b73c2247212eca0d19b70a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 18:17:14 +0000 Subject: [PATCH 01/10] Initial plan From 0b2599175c63cf12cbaf2686a0f5a7f97f4c6524 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 18:23:37 +0000 Subject: [PATCH 02/10] Deduplicate unsigned integer warning reporting Agent-Logs-Url: https://github.com/SpineEventEngine/validation/sessions/68b0cb8a-7b52-4402-9110-5252ffe52f56 Co-authored-by: alexander-yevsyukov <3116444+alexander-yevsyukov@users.noreply.github.com> --- .../option/bound/BoundedFieldGenerator.kt | 47 +++++++++---- .../bound/UnsignedIntegerWarningsSpec.kt | 69 +++++++++++++++++++ 2 files changed, 103 insertions(+), 13 deletions(-) create mode 100644 java/src/test/kotlin/io/spine/tools/validation/java/generate/option/bound/UnsignedIntegerWarningsSpec.kt diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt index 00926ecddb..9bc5e0507c 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt @@ -69,6 +69,7 @@ import io.spine.tools.validation.java.generate.option.bound.Docs.SCALAR_TYPES import io.spine.tools.validation.java.generate.option.bound.Docs.UNSIGNED_API import io.spine.type.TypeName import io.spine.validation.ConstraintViolation +import java.util.concurrent.ConcurrentHashMap /** * An abstract base for field generators that restrict the range of numeric fields. @@ -120,11 +121,9 @@ internal abstract class BoundedFieldGenerator( * of [ConstraintViolation] and adds it to the [violations] list. */ private fun checkWithinBounds(value: Expression): CodeBlock { - // TODO:2025-05-12:yevhenii.nadtochii: Enable reporting back when we decide upon the format. - // Issue: https://github.com/SpineEventEngine/validation/issues/227. - // if (boundPrimitive in listOf(UINT32_VALUE, UINT64_VALUE)) { - // unsignedIntegerWarning(view.file, field.span) - // } + if (boundPrimitive in listOf(UINT32_VALUE, UINT64_VALUE)) { + unsignedIntegerWarning(view.file, field.span) + } return CodeBlock( """ if (${isOutOfBounds(value)}) { @@ -217,15 +216,16 @@ internal abstract class BoundedFieldGenerator( } } -@Suppress("unused") // https://github.com/SpineEventEngine/validation/issues/227 private fun unsignedIntegerWarning(file: File, span: Span) = - Compilation.warning(file, span) { - "Unsigned integer types are not supported in Java. The Protobuf compiler uses" + - " signed integers to represent unsigned types in Java ($SCALAR_TYPES)." + - " Operations on unsigned values rely on static utility methods from" + - " `$IntegerClass` and `$LongClass` classes ($UNSIGNED_API). Be cautious" + - " when dealing with unsigned values outside of these methods, as Java" + - " treats all primitive integers as signed." + UnsignedIntegerWarnings.report(file, span) { + Compilation.warning(file, span) { + "Unsigned integer types are not supported in Java. The Protobuf compiler uses" + + " signed integers to represent unsigned types in Java ($SCALAR_TYPES)." + + " Operations on unsigned values rely on static utility methods from" + + " `$IntegerClass` and `$LongClass` classes ($UNSIGNED_API). Be cautious" + + " when dealing with unsigned values outside of these methods, as Java" + + " treats all primitive integers as signed." + } } @Suppress("MaxLineLength") // Long links. @@ -234,3 +234,24 @@ private object Docs { const val UNSIGNED_API = "https://www.baeldung.com/java-unsigned-arithmetic#the-unsigned-integer-api" } + +internal object UnsignedIntegerWarnings { + + private val reported = ConcurrentHashMap.newKeySet() + + fun report(file: File, span: Span, warning: () -> Unit) { + report(key(file, span), warning) + } + + internal fun report(key: String, warning: () -> Unit) { + if (reported.add(key)) { + warning() + } + } + + internal fun clear() { + reported.clear() + } + + private fun key(file: File, span: Span): String = "$file:$span" +} diff --git a/java/src/test/kotlin/io/spine/tools/validation/java/generate/option/bound/UnsignedIntegerWarningsSpec.kt b/java/src/test/kotlin/io/spine/tools/validation/java/generate/option/bound/UnsignedIntegerWarningsSpec.kt new file mode 100644 index 0000000000..1c3251ddcb --- /dev/null +++ b/java/src/test/kotlin/io/spine/tools/validation/java/generate/option/bound/UnsignedIntegerWarningsSpec.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.tools.validation.java.generate.option.bound + +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("`UnsignedIntegerWarnings` should") +internal class UnsignedIntegerWarningsSpec { + + @BeforeEach + fun setUp() { + UnsignedIntegerWarnings.clear() + } + + @Test + fun `report warning only once for the same location`() { + var reports = 0 + + UnsignedIntegerWarnings.report("same-location") { + reports++ + } + UnsignedIntegerWarnings.report("same-location") { + reports++ + } + + reports shouldBe 1 + } + + @Test + fun `report warning for each distinct location`() { + var reports = 0 + + UnsignedIntegerWarnings.report("location-1") { + reports++ + } + UnsignedIntegerWarnings.report("location-2") { + reports++ + } + + reports shouldBe 2 + } +} From 05cd91d81e5afd45677fe138072024009bb47ef5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 19:36:10 +0000 Subject: [PATCH 03/10] Add KDoc for unsigned warning deduplicator Agent-Logs-Url: https://github.com/SpineEventEngine/validation/sessions/e964dc0d-3376-4fb4-bdb2-ae3dce54cab8 Co-authored-by: alexander-yevsyukov <3116444+alexander-yevsyukov@users.noreply.github.com> --- .../java/generate/option/bound/BoundedFieldGenerator.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt index 9bc5e0507c..8b3a16a282 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt @@ -235,6 +235,9 @@ private object Docs { "https://www.baeldung.com/java-unsigned-arithmetic#the-unsigned-integer-api" } +/** + * Reports unsigned-integer warnings once per source location. + */ internal object UnsignedIntegerWarnings { private val reported = ConcurrentHashMap.newKeySet() From 1f31e6009b256ca7b69847495930e7f2c47a8121 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 19:46:00 +0000 Subject: [PATCH 04/10] Mark test hooks visible and drop redundant internal modifiers Agent-Logs-Url: https://github.com/SpineEventEngine/validation/sessions/35bfa4c1-1992-4221-b4ab-a9c658269d4c Co-authored-by: alexander-yevsyukov <3116444+alexander-yevsyukov@users.noreply.github.com> --- .../java/generate/option/bound/BoundedFieldGenerator.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt index 8b3a16a282..91d2001787 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt @@ -27,6 +27,7 @@ package io.spine.tools.validation.java.generate.option.bound import io.spine.base.FieldPath +import io.spine.annotation.VisibleForTesting import io.spine.string.camelCase import io.spine.tools.compiler.Compilation import io.spine.tools.compiler.ast.FieldType @@ -246,13 +247,15 @@ internal object UnsignedIntegerWarnings { report(key(file, span), warning) } - internal fun report(key: String, warning: () -> Unit) { + @VisibleForTesting + fun report(key: String, warning: () -> Unit) { if (reported.add(key)) { warning() } } - internal fun clear() { + @VisibleForTesting + fun clear() { reported.clear() } From 02cd3d8d42ef08480843738551dfc0694a0cc820 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 19:46:33 +0000 Subject: [PATCH 05/10] Sort imports in bounded warning generator Agent-Logs-Url: https://github.com/SpineEventEngine/validation/sessions/35bfa4c1-1992-4221-b4ab-a9c658269d4c Co-authored-by: alexander-yevsyukov <3116444+alexander-yevsyukov@users.noreply.github.com> --- .../java/generate/option/bound/BoundedFieldGenerator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt index 91d2001787..aa9b3c6674 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/bound/BoundedFieldGenerator.kt @@ -26,8 +26,8 @@ package io.spine.tools.validation.java.generate.option.bound -import io.spine.base.FieldPath import io.spine.annotation.VisibleForTesting +import io.spine.base.FieldPath import io.spine.string.camelCase import io.spine.tools.compiler.Compilation import io.spine.tools.compiler.ast.FieldType From 8325afccf695f9a93d1a89ee63f8db94957edd14 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 20:41:38 +0000 Subject: [PATCH 06/10] Bump version to 2.0.0-SNAPSHOT.421 Agent-Logs-Url: https://github.com/SpineEventEngine/validation/sessions/f90f333f-cd83-44ed-9a22-42cbbad478ae Co-authored-by: alexander-yevsyukov <3116444+alexander-yevsyukov@users.noreply.github.com> --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index aae97b27a8..82a46bd6ca 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -27,4 +27,4 @@ /** * The version of the Validation library to publish. */ -val validationVersion by extra("2.0.0-SNAPSHOT.419") +val validationVersion by extra("2.0.0-SNAPSHOT.421") From 499c4d4679ade7ada8542d3a42a76111f0b6f838 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 20:50:43 +0000 Subject: [PATCH 07/10] Update dependency reports Agent-Logs-Url: https://github.com/SpineEventEngine/validation/sessions/f7e81e60-437d-4fd5-a6c4-7549d5cd987a Co-authored-by: alexander-yevsyukov <3116444+alexander-yevsyukov@users.noreply.github.com> --- docs/dependencies/dependencies.md | 30 +++++++++++++++--------------- docs/dependencies/pom.xml | 2 +- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md index 041de692fe..ff071b1499 100644 --- a/docs/dependencies/dependencies.md +++ b/docs/dependencies/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1097,7 +1097,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1798,7 +1798,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-docs:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-docs:2.0.0-SNAPSHOT.421` ## Runtime ## Compile, tests, and tooling @@ -1812,7 +1812,7 @@ This report was generated on **Wed May 13 20:09:04 WEST 2026** using -# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -2871,7 +2871,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -3968,7 +3968,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : org.jetbrains. **Name** : annotations. **Version** : 13.0. @@ -4022,7 +4022,7 @@ This report was generated on **Wed May 13 20:44:35 WEST 2026** using -# Dependencies of `io.spine:spine-validation-jvm-runtime:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine:spine-validation-jvm-runtime:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -4829,7 +4829,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -5518,7 +5518,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -5983,7 +5983,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -6609,7 +6609,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -7177,7 +7177,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -7788,7 +7788,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -8533,7 +8533,7 @@ This report was generated on **Wed May 13 20:44:36 WEST 2026** using -# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -8773,7 +8773,7 @@ This report was generated on **Wed May 13 20:44:35 WEST 2026** using -# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.420` +# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.421` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. diff --git a/docs/dependencies/pom.xml b/docs/dependencies/pom.xml index c57129ad5c..133b9969ac 100644 --- a/docs/dependencies/pom.xml +++ b/docs/dependencies/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.tools validation -2.0.0-SNAPSHOT.420 +2.0.0-SNAPSHOT.421 2015 From 14d262acf16528fdfb331c230106584cf61b4544 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 21:16:24 +0000 Subject: [PATCH 08/10] Initial plan From 7af3d54c9af2314e3801f6f5b445962156d7bc45 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 21:26:53 +0000 Subject: [PATCH 09/10] Agent-Logs-Url: https://github.com/SpineEventEngine/validation/sessions/b81d4af0-e5e0-46d0-adb7-40a8f1c67aa7 Co-authored-by: alexander-yevsyukov <3116444+alexander-yevsyukov@users.noreply.github.com> --- docs/content/docs/validation/developer/build-and-release.md | 2 +- .../docs/validation/user/01-getting-started/adding-to-build.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/docs/validation/developer/build-and-release.md b/docs/content/docs/validation/developer/build-and-release.md index 6ef2ecc511..dc0ca90609 100644 --- a/docs/content/docs/validation/developer/build-and-release.md +++ b/docs/content/docs/validation/developer/build-and-release.md @@ -47,7 +47,7 @@ through Gradle's `extra` properties: end="val validationVersion"> ```kotlin -val validationVersion by extra("2.0.0-SNAPSHOT.420") +val validationVersion by extra("2.0.0-SNAPSHOT.421") ``` The root build script applies this file under `allprojects { … }` and assigns diff --git a/docs/content/docs/validation/user/01-getting-started/adding-to-build.md b/docs/content/docs/validation/user/01-getting-started/adding-to-build.md index d5bf9d49fc..ab88f3805d 100644 --- a/docs/content/docs/validation/user/01-getting-started/adding-to-build.md +++ b/docs/content/docs/validation/user/01-getting-started/adding-to-build.md @@ -90,7 +90,7 @@ Add the Validation plugin to the build. ```kotlin plugins { module - id("io.spine.validation") version "2.0.0-SNAPSHOT.420" + id("io.spine.validation") version "2.0.0-SNAPSHOT.421" } ``` From 397f226b576b7acdbead8fb2b62f2cb3e4d24e7d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 13 May 2026 21:28:52 +0000 Subject: [PATCH 10/10] Add regression coverage for primitive assumed IDs Agent-Logs-Url: https://github.com/SpineEventEngine/validation/sessions/c0c59d00-5e83-41d0-8488-c2618a6ea31a Co-authored-by: alexander-yevsyukov <3116444+alexander-yevsyukov@users.noreply.github.com> --- .../tools/validation/AssumedRequiredIdSpec.kt | 94 +++++++++++++++++++ .../validation/assumed_required_id_spec.proto | 42 +++++++++ .../java/generate/option/RequiredGenerator.kt | 4 +- .../test/options/AssumedRequiredITest.kt | 9 +- .../spine/test/tools/validate/entity.proto | 12 ++- 5 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 context-tests/src/test/kotlin/io/spine/tools/validation/AssumedRequiredIdSpec.kt create mode 100644 context-tests/src/testFixtures/proto/spine/validation/assumed_required_id_spec.proto diff --git a/context-tests/src/test/kotlin/io/spine/tools/validation/AssumedRequiredIdSpec.kt b/context-tests/src/test/kotlin/io/spine/tools/validation/AssumedRequiredIdSpec.kt new file mode 100644 index 0000000000..478865aa3b --- /dev/null +++ b/context-tests/src/test/kotlin/io/spine/tools/validation/AssumedRequiredIdSpec.kt @@ -0,0 +1,94 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package io.spine.tools.validation + +import com.google.protobuf.Descriptors.Descriptor +import io.kotest.matchers.string.shouldNotContain +import io.spine.logging.testing.ConsoleTap +import io.spine.logging.testing.tapConsole +import io.spine.testing.compiler.PipelineSetup +import io.spine.testing.compiler.acceptingOnly +import io.spine.testing.compiler.pipelineParams +import io.spine.testing.compiler.withRequestFile +import io.spine.testing.compiler.withSettingsDir +import io.spine.tools.code.SourceSetName +import io.spine.tools.compiler.params.WorkingDirectory +import io.spine.tools.compiler.protobuf.field +import io.spine.tools.validation.given.EntityWithInt32Id +import java.nio.file.Path +import kotlin.io.path.createDirectories +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +@DisplayName("Assumed `(required)` entity IDs should") +internal class AssumedRequiredIdSpec { + + @TempDir + lateinit var workingDir: Path + + @Test + fun `not emit a warning for an int32 field`() { + val descriptor = EntityWithInt32Id.getDescriptor() + val output = compile(descriptor) + val field = descriptor.field("id") + + output shouldNotContain field.fullName + output shouldNotContain "should not be declared as `(required)`" + output shouldNotContain "assumed to be required" + } + + private fun compile(descriptor: Descriptor): String { + val wd = WorkingDirectory(workingDir) + val outputDir = workingDir.resolve("output") + outputDir.createDirectories() + val params = pipelineParams { + withRequestFile(wd.requestDirectory.file(SourceSetName("testFixtures"))) + withSettingsDir(wd.settingsDirectory.path) + } + val setup = PipelineSetup.byResources( + params, + plugins = listOf(object : ValidationPlugin() {}), + outputRoot = outputDir, + descriptorFilter = acceptingOnly(descriptor) + ) {} + val pipeline = setup.createPipeline() + return tapConsole { + pipeline() + } + } + + companion object { + + @JvmStatic + @BeforeAll + fun installConsoleTap() { + ConsoleTap.install() + } + } +} diff --git a/context-tests/src/testFixtures/proto/spine/validation/assumed_required_id_spec.proto b/context-tests/src/testFixtures/proto/spine/validation/assumed_required_id_spec.proto new file mode 100644 index 0000000000..c574431880 --- /dev/null +++ b/context-tests/src/testFixtures/proto/spine/validation/assumed_required_id_spec.proto @@ -0,0 +1,42 @@ +/* + * Copyright 2026, TeamDev. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +syntax = "proto3"; + +package spine.validation.stubs; + +import "spine/options.proto"; + +option (type_url_prefix) = "type.spine.io"; +option java_package = "io.spine.tools.validation.given"; +option java_outer_classname = "AssumedRequiredIdSpecProto"; +option java_multiple_files = true; + +message EntityWithInt32Id { + option (entity).kind = ENTITY; + + int32 id = 1; +} diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/RequiredGenerator.kt b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/RequiredGenerator.kt index 61948ece2b..b96c14b8f7 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/RequiredGenerator.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/generate/option/RequiredGenerator.kt @@ -1,5 +1,5 @@ /* - * Copyright 2025, TeamDev. All rights reserved. + * Copyright 2026, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,6 +51,7 @@ import io.spine.tools.validation.java.generate.SingleOptionCode import io.spine.tools.validation.java.generate.ValidateScope.parentName import io.spine.tools.validation.java.generate.ValidateScope.parentPath import io.spine.tools.validation.java.generate.ValidateScope.violations +import io.spine.tools.validation.option.required.RequiredFieldSupport.isSupported import io.spine.validation.ConstraintViolation import io.spine.tools.validation.option.IF_MISSING import io.spine.tools.validation.RequiredField @@ -71,6 +72,7 @@ internal class RequiredGenerator : OptionGeneratorWithConverter() { override fun codeFor(type: TypeName): List = allRequiredFields .filter { it.id.type == type } + .filter { it.subject.type.isSupported() } .map { GenerateRequired(it, converter).code() } } diff --git a/tests/validating/src/test/kotlin/io/spine/test/options/AssumedRequiredITest.kt b/tests/validating/src/test/kotlin/io/spine/test/options/AssumedRequiredITest.kt index 418344e4fd..015ad04c63 100644 --- a/tests/validating/src/test/kotlin/io/spine/test/options/AssumedRequiredITest.kt +++ b/tests/validating/src/test/kotlin/io/spine/test/options/AssumedRequiredITest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2024, TeamDev. All rights reserved. + * Copyright 2026, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,6 +30,7 @@ import io.spine.base.Identifier import io.spine.test.tools.validate.TaskId import io.spine.test.tools.validate.command.AssignTask import io.spine.test.tools.validate.command.CreateProject +import io.spine.test.tools.validate.entity.NumberStats import io.spine.test.tools.validate.entity.Project import io.spine.test.tools.validate.entity.Task import io.spine.tools.validation.assertions.assertInvalid @@ -85,5 +86,11 @@ internal class AssumedRequiredITest { val msg = Task.newBuilder() assertValid(msg) } + + @Test + fun `not requiring a primitive ID`() { + val msg = NumberStats.newBuilder() + assertValid(msg) + } } } diff --git a/tests/validating/src/testFixtures/proto/spine/test/tools/validate/entity.proto b/tests/validating/src/testFixtures/proto/spine/test/tools/validate/entity.proto index db2d77a63a..b868dc2ee5 100644 --- a/tests/validating/src/testFixtures/proto/spine/test/tools/validate/entity.proto +++ b/tests/validating/src/testFixtures/proto/spine/test/tools/validate/entity.proto @@ -1,11 +1,11 @@ /* - * Copyright 2024, TeamDev. All rights reserved. + * Copyright 2026, TeamDev. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Redistribution and use in source and/or binary forms, with or without * modification, must retain the above copyright notice and the following @@ -23,6 +23,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + syntax = "proto3"; package spine.test.tools.validate; @@ -52,3 +53,10 @@ message Task { // Here the first field (which is assumed to be required) is explicitly set optional. string id = 1 [(required) = false]; } + +// An entity type with a primitive ID. +message NumberStats { + option (entity).kind = ENTITY; + + int32 id = 1; +}