This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch 
test/document-datamapping-core-transformers
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 478d0cd3c76a2c365b3cebf9a63e325588f2fc7c
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Thu Aug 13 11:21:38 2026 -0500

    Add test coverage for AbstractTraitApplyingGormASTTransformation's default 
trait-weaving path
    
    AbstractTraitApplyingGormASTTransformation sat at 57% coverage. Its
    only concrete subclass in this module, ServiceTransformation,
    overrides shouldWeave with its own logic and calls the static
    weaveTraitWithGenerics directly rather than through the instance
    weaveTrait method - so the base class's own default behavior (shouldWeave
    returning true, weaveTrait delegating to Groovy's TraitComposer, and
    several weaveTraitWithGenerics edge branches: no-generics traits,
    interface class nodes, and partial/full generic-arity mismatches) was
    never exercised.
    
    Add a spec covering these directly: the generics edge cases against
    bare ClassNodes (same technique as the sibling
    AbstractGormASTTransformationSpec), and the instance weaveTrait method
    - including the real TraitComposer.doExtendTraits call - by compiling
    a class through a test-only local transform (TestTraitWeavingTransformation,
    applied via ApplyTestTraitWeaving) and asserting the compiled class
    actually gained the woven trait's method, following the same
    local-transform-testing pattern used for DetachedCriteriaASTTransformation
    earlier in this branch.
    
    Coverage moves 57% -> 98% instruction, 40% -> 72% branch.
    
    Co-Authored-By: Claude Sonnet 5 <[email protected]>
---
 ...ctTraitApplyingGormASTTransformationSpec.groovy | 170 +++++++++++++++++++++
 .../gorm/transform/ApplyTestTraitWeaving.java      |  38 +++++
 .../gorm/transform/DoubleGenericTestTrait.groovy   |  37 +++++
 .../gorm/transform/SingleGenericTestTrait.groovy   |  31 ++++
 .../TestTraitWeavingTransformation.groovy          |  61 ++++++++
 .../gorm/transform/TestWeavableTrait.groovy        |  31 ++++
 6 files changed, 368 insertions(+)

diff --git 
a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/AbstractTraitApplyingGormASTTransformationSpec.groovy
 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/AbstractTraitApplyingGormASTTransformationSpec.groovy
new file mode 100644
index 0000000000..c0b96170b7
--- /dev/null
+++ 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/AbstractTraitApplyingGormASTTransformationSpec.groovy
@@ -0,0 +1,170 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.datastore.gorm.transform
+
+import java.lang.reflect.Modifier
+
+import org.codehaus.groovy.ast.AnnotationNode
+import org.codehaus.groovy.ast.ClassHelper
+import org.codehaus.groovy.ast.ClassNode
+
+import spock.lang.Specification
+
+/**
+ * {@code AbstractTraitApplyingGormASTTransformation} is only ever exercised 
in this module through
+ * its one concrete subclass, {@code ServiceTransformation}, which overrides 
{@code shouldWeave}
+ * with its own logic (never delegating to the default) and, for the 
interface/abstract-class case it
+ * actually tests, calls the static {@code weaveTraitWithGenerics} directly 
rather than going through
+ * the instance {@code weaveTrait} method. That leaves this base class's own 
default behavior -
+ * {@code shouldWeave} returning {@code true}, the instance {@code weaveTrait} 
delegating to
+ * {@code TraitComposer.doExtendTraits}, and several edge branches of {@code 
weaveTraitWithGenerics}
+ * (a trait with no generic type parameters, an interface class node that must 
be skipped entirely,
+ * and a trait whose generic arity does not match - fully or partially - the 
number of arguments
+ * supplied) - unexercised. These specs cover that gap directly: the {@code 
weaveTraitWithGenerics}
+ * edge branches and the default {@code shouldWeave} are tested against bare, 
detached
+ * {@code ClassNode}s (the technique used in {@link 
AbstractGormASTTransformationSpec}), while the
+ * instance {@code weaveTrait} method - and the real {@code 
TraitComposer.doExtendTraits}
+ * call it makes - is proven by compiling a real class through {@link 
TestTraitWeavingTransformation},
+ * a test-only local transform applied via {@link ApplyTestTraitWeaving}, and 
asserting the compiled
+ * class actually gained the trait's method.
+ */
+class AbstractTraitApplyingGormASTTransformationSpec extends Specification {
+
+    static class MinimalTraitTransformation extends 
AbstractTraitApplyingGormASTTransformation {
+
+        @Override
+        protected Class getTraitClass() {
+            TestWeavableTrait
+        }
+
+        @Override
+        protected ClassNode getAnnotationType() {
+            ClassHelper.make(ApplyTestTraitWeaving)
+        }
+
+        @Override
+        protected Object getAppliedMarker() {
+            new Object()
+        }
+
+        @Override
+        int priority() {
+            0
+        }
+    }
+
+    private static ClassNode newTargetClassNode(String name) {
+        new ClassNode(name, 0, ClassHelper.OBJECT_TYPE)
+    }
+
+    void "shouldWeave defaults to true when a subclass does not override it"() 
{
+        given:
+        MinimalTraitTransformation transformation = new 
MinimalTraitTransformation()
+        ClassNode targetClassNode = 
newTargetClassNode('org.grails.datastore.gorm.transform.fixture.ShouldWeaveTarget')
+        AnnotationNode annotationNode = new 
AnnotationNode(ClassHelper.make(ApplyTestTraitWeaving))
+
+        expect:
+        transformation.shouldWeave(annotationNode, targetClassNode)
+    }
+
+    void "weaveTraitWithGenerics adds the plain interface when the trait 
declares no generic type parameters"() {
+        given:
+        ClassNode targetClassNode = 
newTargetClassNode('org.grails.datastore.gorm.transform.fixture.NoGenericsWeaveTarget')
+
+        expect:
+        
!targetClassNode.implementsInterface(ClassHelper.make(TestWeavableTrait))
+
+        when:
+        
AbstractTraitApplyingGormASTTransformation.weaveTraitWithGenerics(targetClassNode,
 TestWeavableTrait)
+
+        then:
+        
targetClassNode.implementsInterface(ClassHelper.make(TestWeavableTrait))
+    }
+
+    void "weaveTraitWithGenerics pads missing generic arguments with Object 
when fewer arguments are supplied than the trait declares"() {
+        given:
+        ClassNode targetClassNode = 
newTargetClassNode('org.grails.datastore.gorm.transform.fixture.GenericsPaddingWeaveTarget')
+        ClassNode traitClassNode = ClassHelper.make(SingleGenericTestTrait)
+
+        expect:
+        !targetClassNode.implementsInterface(traitClassNode)
+
+        when: 'weaving with zero generic arguments even though the trait 
declares one'
+        
AbstractTraitApplyingGormASTTransformation.weaveTraitWithGenerics(targetClassNode,
 SingleGenericTestTrait)
+
+        then: 'the missing generic argument slot is padded with Object'
+        targetClassNode.implementsInterface(traitClassNode)
+        ClassNode wovenInterface = targetClassNode.interfaces.find { it.name 
== SingleGenericTestTrait.name }
+        wovenInterface.genericsTypes.length == 1
+        wovenInterface.genericsTypes[0].type == ClassHelper.OBJECT_TYPE
+    }
+
+    void "weaveTraitWithGenerics is a no-op for an interface class node"() {
+        given: 'a class node that is itself an interface'
+        ClassNode interfaceClassNode = new ClassNode(
+                
'org.grails.datastore.gorm.transform.fixture.NoWeaveInterfaceTarget',
+                Modifier.PUBLIC | Modifier.INTERFACE,
+                ClassHelper.OBJECT_TYPE)
+
+        expect:
+        interfaceClassNode.interface
+
+        when:
+        
AbstractTraitApplyingGormASTTransformation.weaveTraitWithGenerics(interfaceClassNode,
 TestWeavableTrait)
+
+        then: 'the trait is never added because interfaces are skipped 
entirely'
+        
!interfaceClassNode.implementsInterface(ClassHelper.make(TestWeavableTrait))
+        interfaceClassNode.interfaces.length == 0
+    }
+
+    void "weaveTraitWithGenerics pads only the missing slots when some, but 
not all, generic arguments are supplied"() {
+        given:
+        ClassNode targetClassNode = 
newTargetClassNode('org.grails.datastore.gorm.transform.fixture.PartialGenericsWeaveTarget')
+        ClassNode traitClassNode = ClassHelper.make(DoubleGenericTestTrait)
+        ClassNode suppliedArgument = ClassHelper.make(String)
+
+        when: 'only the first of the two declared generic arguments is 
supplied'
+        
AbstractTraitApplyingGormASTTransformation.weaveTraitWithGenerics(targetClassNode,
 DoubleGenericTestTrait, suppliedArgument)
+
+        then: 'the supplied argument is used for the first slot and Object 
pads the second'
+        targetClassNode.implementsInterface(traitClassNode)
+        ClassNode wovenInterface = targetClassNode.interfaces.find { it.name 
== DoubleGenericTestTrait.name }
+        wovenInterface.genericsTypes.length == 2
+        wovenInterface.genericsTypes[0].type == ClassHelper.make(String)
+        wovenInterface.genericsTypes[1].type == ClassHelper.OBJECT_TYPE
+    }
+
+    void "weaveTrait composes the trait onto the class via a real compilation 
unit"() {
+        given:
+        GroovyClassLoader classLoader = new 
GroovyClassLoader(getClass().classLoader)
+
+        when:
+        Class<?> compiled = classLoader.parseClass('''
+            package org.grails.datastore.gorm.transform.fixture
+
+            @org.grails.datastore.gorm.transform.ApplyTestTraitWeaving
+            class WeavingTarget {
+            }
+        ''')
+
+        then: 'the class implements the woven trait and the trait method was 
composed into it'
+        TestWeavableTrait.isAssignableFrom(compiled)
+        compiled.getDeclaredConstructor().newInstance().testTraitMarkerValue() 
== 'test-trait-woven'
+    }
+}
diff --git 
a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/ApplyTestTraitWeaving.java
 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/ApplyTestTraitWeaving.java
new file mode 100644
index 0000000000..f97b75ed03
--- /dev/null
+++ 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/ApplyTestTraitWeaving.java
@@ -0,0 +1,38 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+package org.grails.datastore.gorm.transform;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.codehaus.groovy.transform.GroovyASTTransformationClass;
+
+/**
+ * Local marker annotation used only for testing {@link 
AbstractTraitApplyingGormASTTransformation}
+ * through a real, live compilation - triggering {@link 
TestTraitWeavingTransformation} so that the
+ * annotated class is woven with {@link TestWeavableTrait} via a genuine 
{@code CompilationUnit}.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+@GroovyASTTransformationClass("org.grails.datastore.gorm.transform.TestTraitWeavingTransformation")
+public @interface ApplyTestTraitWeaving {
+}
diff --git 
a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/DoubleGenericTestTrait.groovy
 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/DoubleGenericTestTrait.groovy
new file mode 100644
index 0000000000..c1939e71db
--- /dev/null
+++ 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/DoubleGenericTestTrait.groovy
@@ -0,0 +1,37 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.datastore.gorm.transform
+
+/**
+ * A trait with two declared generic type parameters, used only to exercise
+ * {@link AbstractTraitApplyingGormASTTransformation#weaveTraitWithGenerics} 
in tests where only
+ * some, but not all, of the declared generic type parameters are supplied 
with an argument -
+ * so that both the "argument supplied" and "argument padded with Object" code 
paths run in the
+ * same call.
+ */
+trait DoubleGenericTestTrait<A, B> {
+
+    A firstValue(A value) {
+        value
+    }
+
+    B secondValue(B value) {
+        value
+    }
+}
diff --git 
a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/SingleGenericTestTrait.groovy
 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/SingleGenericTestTrait.groovy
new file mode 100644
index 0000000000..266d12f9ad
--- /dev/null
+++ 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/SingleGenericTestTrait.groovy
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.datastore.gorm.transform
+
+/**
+ * A trait with exactly one declared generic type parameter, used only to 
exercise
+ * {@link AbstractTraitApplyingGormASTTransformation#weaveTraitWithGenerics} 
in tests where the
+ * number of supplied generic arguments does not match the number the trait 
declares.
+ */
+trait SingleGenericTestTrait<T> {
+
+    T identity(T value) {
+        value
+    }
+}
diff --git 
a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/TestTraitWeavingTransformation.groovy
 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/TestTraitWeavingTransformation.groovy
new file mode 100644
index 0000000000..eaaff25423
--- /dev/null
+++ 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/TestTraitWeavingTransformation.groovy
@@ -0,0 +1,61 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.datastore.gorm.transform
+
+import org.codehaus.groovy.ast.AnnotationNode
+import org.codehaus.groovy.ast.ClassHelper
+import org.codehaus.groovy.ast.ClassNode
+import org.codehaus.groovy.control.CompilePhase
+import org.codehaus.groovy.transform.ASTTransformation
+import org.codehaus.groovy.transform.GroovyASTTransformation
+
+/**
+ * Local, annotation-driven transformation used only to test {@link 
AbstractTraitApplyingGormASTTransformation}
+ * through a genuine {@code CompilationUnit}. It deliberately does not 
override {@code shouldWeave},
+ * so the default implementation runs, and it weaves {@link TestWeavableTrait} 
- a trait with no
+ * generic type parameters - onto whatever class is annotated with {@link 
ApplyTestTraitWeaving}.
+ *
+ * @see AbstractTraitApplyingGormASTTransformationSpec
+ */
+@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS)
+class TestTraitWeavingTransformation extends 
AbstractTraitApplyingGormASTTransformation implements ASTTransformation {
+
+    private static final ClassNode MY_TYPE = 
ClassHelper.make(ApplyTestTraitWeaving)
+    private static final Object APPLIED_MARKER = new Object()
+
+    @Override
+    protected Class getTraitClass() {
+        TestWeavableTrait
+    }
+
+    @Override
+    protected ClassNode getAnnotationType() {
+        MY_TYPE
+    }
+
+    @Override
+    protected Object getAppliedMarker() {
+        APPLIED_MARKER
+    }
+
+    @Override
+    int priority() {
+        0
+    }
+}
diff --git 
a/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/TestWeavableTrait.groovy
 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/TestWeavableTrait.groovy
new file mode 100644
index 0000000000..d45b896f52
--- /dev/null
+++ 
b/grails-datamapping-core/src/test/groovy/org/grails/datastore/gorm/transform/TestWeavableTrait.groovy
@@ -0,0 +1,31 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you 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
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.grails.datastore.gorm.transform
+
+/**
+ * A trait with no generic type parameters, used only to exercise
+ * {@link AbstractTraitApplyingGormASTTransformation} in tests: weaving a 
trait that declares no
+ * generics onto a target class node.
+ */
+trait TestWeavableTrait {
+
+    String testTraitMarkerValue() {
+        'test-trait-woven'
+    }
+}

Reply via email to