org.gradle.model.internal.inspect.ModelRuleExtractorTest.groovy Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.9.1 API redistribution.
/*
* Copyright 2014 the original author or authors.
*
* 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
*
* 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.gradle.model.internal.inspect
import org.gradle.model.*
import org.gradle.model.internal.core.*
import org.gradle.model.internal.fixture.ProjectRegistrySpec
import org.gradle.model.internal.manage.schema.extract.InvalidManagedModelElementTypeException
import org.gradle.model.internal.manage.schema.extract.ModelStoreTestUtils
import org.gradle.model.internal.registry.DefaultModelRegistry
import org.gradle.model.internal.registry.ModelRegistry
import org.gradle.model.internal.type.ModelType
import org.gradle.test.fixtures.ConcurrentTestUtil
import spock.lang.Unroll
import java.beans.Introspector
import static org.gradle.model.ModelTypeTesting.fullyQualifiedNameOf
class ModelRuleExtractorTest extends ProjectRegistrySpec {
def extractor = new ModelRuleExtractor(MethodModelRuleExtractors.coreExtractors(SCHEMA_STORE), MANAGED_PROXY_FACTORY, SCHEMA_STORE, STRUCT_BINDINGS_STORE)
ModelRegistry registry = new DefaultModelRegistry(extractor, null)
static class ModelThing {
final String name
ModelThing(String name) {
this.name = name
}
}
static class EmptyClass extends RuleSource {}
def "can inspect class with no rules"() {
expect:
extract(EmptyClass).empty
}
static class ClassWithNonRuleMethods extends RuleSource {
static List thing() {
[]
}
static List genericThing() {
[]
}
private doStuff() {}
private T selectThing(List list) { null }
}
def "can have non-rule methods that would be invalid rules"() {
expect:
extract(ClassWithNonRuleMethods).empty
}
static abstract class AbstractRules extends RuleSource {}
def "rule class can be abstract"() {
expect:
extract(AbstractRules).empty
}
def "can create instance of abstract rule class"() {
expect:
def schema = extractor.extract(AbstractRules)
schema.factory.create() instanceof AbstractRules
}
static abstract class AbstractPropertyRules extends RuleSource {
@RuleInput
abstract String getValue()
abstract void setValue(String value)
@RuleInput
abstract int getNumber()
abstract void setNumber(int value)
}
def "rule class can have abstract getter and setter"() {
expect:
extract(AbstractPropertyRules).empty
}
def "can create instance of rule class with abstract getter and setter"() {
when:
def schema = extractor.extract(AbstractPropertyRules)
def instance = schema.factory.create()
then:
instance instanceof AbstractPropertyRules
instance.value == null
instance.number == 0
when:
instance.value = "12"
instance.number = 12
then:
instance.value == "12"
instance.number == 12
}
def "state is reused for all instances creates from a given extracted rule source"() {
given:
def schema = extractor.extract(AbstractPropertyRules)
def instance = schema.factory.create()
instance.value = "12"
instance.number = 12
expect:
def sameSchema = schema.factory.create()
sameSchema.value == "12"
sameSchema.number == 12
def schema2 = extractor.extract(AbstractPropertyRules)
def differentSchema = schema2.factory.create()
differentSchema.value == null
differentSchema.number == 0
}
def "Java rule class can have non-public getters, setters and rule methods"() {
expect:
def schema = extractor.extract(AbstractJavaPropertyRules)
schema.rules.size() == 2
schema.factory.create() != null
}
static abstract class AbstractMethodsRules extends RuleSource {
@Mutate
abstract void thing(String s)
}
static class NotRuleSource {
}
@Managed
static abstract class ManagedThing {
}
def "rule class must extend RuleSource"() {
when:
extract(type)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(type)} is not a valid rule source:
- Rule source classes must directly extend org.gradle.model.RuleSource"""
where:
type << [Long, RuleSource, NotRuleSource, ManagedThing]
}
def "rule class cannot have abstract rule methods"() {
when:
extract(AbstractMethodsRules)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(AbstractMethodsRules)} is not a valid rule source:
- Method thing(java.lang.String) is not a valid rule method: A rule method cannot be abstract"""
}
def "rule class cannot have Groovy meta methods"() {
when:
extract(WithGroovyMeta).empty
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(WithGroovyMeta)} is not a valid rule source:
- Method methodMissing(java.lang.String, java.lang.Object) is not a valid rule method: A method that is not annotated as a rule must be private
- Method propertyMissing(java.lang.String) is not a valid rule method: A method that is not annotated as a rule must be private
- Method propertyMissing(java.lang.String, java.lang.Object) is not a valid rule method: A method that is not annotated as a rule must be private"""
}
static class SimpleModelCreationRuleInferredName extends RuleSource {
@Model
static ModelThing modelPath() {
new ModelThing("foo")
}
}
List extract(Class> source) {
extractor.extract(source).rules
}
def "can inspect class with simple model creation rule"() {
def registry = Mock(ModelRegistry)
when:
extractor.extract(SimpleModelCreationRuleInferredName).apply(registry, node())
then:
1 * registry.register(_) >> { ModelRegistration registration ->
assert registration.path.toString() == "modelPath"
assert registration.descriptor.toString() == "ModelRuleExtractorTest.SimpleModelCreationRuleInferredName#modelPath()"
}
0 * _
}
def "can create instance of rule class"() {
expect:
def schema = extractor.extract(SimpleModelCreationRuleInferredName)
schema.factory.create() instanceof SimpleModelCreationRuleInferredName
}
static class ParameterizedModel extends RuleSource {
@Model
List strings() {
Arrays.asList("foo")
}
@Model
List super String> superStrings() {
Arrays.asList("foo")
}
@Model
List extends String> extendsStrings() {
Arrays.asList("foo")
}
@Model
List> wildcard() {
Arrays.asList("foo")
}
}
def "can inspect class with model creation rule for parameterized type"() {
when:
extractor.extract(ParameterizedModel).apply(registry, node())
then:
registry.realizeNode(ModelPath.path("strings")).promise.canBeViewedAs(new ModelType>() {})
registry.realizeNode(ModelPath.path("superStrings")).promise.canBeViewedAs(new ModelType>() {})
registry.realizeNode(ModelPath.path("extendsStrings")).promise.canBeViewedAs(new ModelType>() {})
registry.realizeNode(ModelPath.path("wildcard")).promise.canBeViewedAs(new ModelType>() {})
}
static class HasGenericModelRule extends RuleSource {
@Model
static List thing() {
[]
}
}
def "model creation rule cannot be generic"() {
when:
extract(HasGenericModelRule)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(HasGenericModelRule)} is not a valid rule source:
- Method thing() is not a valid rule method: Cannot have type variables (i.e. cannot be a generic method)"""
}
static class HasMultipleRuleAnnotations extends RuleSource {
@Model
@Mutate
static String thing() {
""
}
}
def "model rule method cannot be annotated with multiple rule annotations"() {
when:
extract(HasMultipleRuleAnnotations)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(HasMultipleRuleAnnotations)} is not a valid rule source:
- Method thing() is not a valid rule method: Can only be one of [annotated with @Model and returning a model element, annotated with @Model and taking a managed model element, annotated with @Defaults, annotated with @Mutate, annotated with @Finalize, annotated with @Validate, annotated with @Rules]"""
}
static class ConcreteGenericModelType extends RuleSource {
@Model
static List strings() {
[]
}
}
def "type variables of model type are captured"() {
when:
extractor.extract(ConcreteGenericModelType).apply(registry, node())
def node = registry.realizeNode(new ModelPath("strings"))
def type = node.adapter.asImmutable(new ModelType>() {}, node, null).type
then:
type.parameterized
type.typeVariables[0] == ModelType.of(String)
}
static class ConcreteGenericModelTypeImplementingGenericInterface extends RuleSource implements HasStrings {
@Model
List strings() {
[]
}
}
def "type variables of model type are captured when method is generic in interface"() {
when:
extractor.extract(ConcreteGenericModelTypeImplementingGenericInterface).apply(registry, node())
def node = registry.realizeNode(new ModelPath("strings"))
def type = node.adapter.asImmutable(new ModelType>() {}, node, null).type
then:
type.parameterized
type.typeVariables[0] == ModelType.of(String)
}
static class GenericMutationRule extends RuleSource {
@Mutate
void mutate(T thing) {}
}
def "mutation rule cannot be generic"() {
when:
extract(GenericMutationRule)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(GenericMutationRule)} is not a valid rule source:
- Method mutate(T) is not a valid rule method: Cannot have type variables (i.e. cannot be a generic method)"""
}
static class NonVoidMutationRule extends RuleSource {
@Mutate
String mutate(String thing) {}
}
def "only void is allowed as return type of a mutation rule"() {
when:
extract(NonVoidMutationRule)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(NonVoidMutationRule)} is not a valid rule source:
- Method mutate(java.lang.String) is not a valid rule method: A method annotated with @Mutate must have void return type."""
}
static class NoSubjectMutationRule extends RuleSource {
@Mutate
void mutate() {}
}
def "mutation rule must have a subject"() {
when:
extract(NoSubjectMutationRule)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(NoSubjectMutationRule)} is not a valid rule source:
- Method mutate() is not a valid rule method: A method annotated with @Mutate must have at least one parameter"""
}
static class RuleWithEmptyInputPath extends RuleSource {
@Model
String create(@Path("") String thing) {}
}
def "path of rule input cannot be empty"() {
when:
extract(RuleWithEmptyInputPath)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(RuleWithEmptyInputPath)} is not a valid rule source:
- Method create(java.lang.String) is not a valid rule method: The declared model element path '' used for parameter 1 is not a valid path: Cannot use an empty string as a model path."""
}
static class RuleWithInvalidInputPath extends RuleSource {
@Model
String create(@Path("!!!!") String thing) {}
}
def "path of rule input has to be valid"() {
when:
extract(RuleWithInvalidInputPath)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(RuleWithInvalidInputPath)} is not a valid rule source:
- Method create(java.lang.String) is not a valid rule method: The declared model element path '!!!!' used for parameter 1 is not a valid path: Model element name '!!!!' has illegal first character '!' (names must start with an ASCII letter or underscore)."""
}
static class MutationRules extends RuleSource {
@Mutate
static void mutate1(List strings) {
}
}
def "mutation rules are registered"() {
given:
def registry = Mock(ModelRegistry)
when:
extractor.extract(MutationRules).apply(registry, node())
then:
1 * registry.configure(ModelActionRole.Mutate, _) >> { ModelActionRole role, ModelAction action ->
assert action.descriptor.toString() == 'ModelRuleExtractorTest.MutationRules#mutate1(List)'
}
0 * registry._
}
static class MutationAndFinalizeRules extends RuleSource {
@Mutate
static void mutate3(List strings) {
strings << 3
}
@Finalize
static void finalize1(List strings) {
strings << "2"
}
@Mutate
static void mutate1(List strings) {
strings << "1"
}
}
def "finalize rules are registered"() {
given:
def registry = Mock(ModelRegistry)
when:
extractor.extract(MutationAndFinalizeRules).apply(registry, node())
then:
1 * registry.configure(ModelActionRole.Finalize, _) >> { ModelActionRole role, ModelAction action ->
assert action.descriptor.toString() == 'ModelRuleExtractorTest.MutationAndFinalizeRules#finalize1(List)'
}
}
def "methods are processed ordered by their to string representation"() {
given:
def registry = Mock(ModelRegistry)
def node = node()
when:
extractor.extract(MutationAndFinalizeRules).apply(registry, node)
then:
1 * registry.configure(ModelActionRole.Finalize, _) >> { ModelActionRole role, ModelAction action ->
assert action.descriptor.toString() == 'ModelRuleExtractorTest.MutationAndFinalizeRules#finalize1(List)'
}
then:
1 * registry.configure(ModelActionRole.Mutate, _) >> { ModelActionRole role, ModelAction action ->
assert action.descriptor.toString() == 'ModelRuleExtractorTest.MutationAndFinalizeRules#mutate1(List)'
}
then:
1 * registry.configure(ModelActionRole.Mutate, _) >> { ModelActionRole role, ModelAction action ->
assert action.descriptor.toString() == 'ModelRuleExtractorTest.MutationAndFinalizeRules#mutate3(List)'
}
0 * registry._
}
static class InvalidModelNameViaAnnotation extends RuleSource {
@Model(" ")
String foo() {
"foo"
}
}
def "invalid model name is not allowed"() {
when:
extract(InvalidModelNameViaAnnotation)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(InvalidModelNameViaAnnotation)} is not a valid rule source:
- Method foo() is not a valid rule method: The declared model element path ' ' is not a valid path: Model element name ' ' has illegal first character ' ' (names must start with an ASCII letter or underscore)."""
}
static class RuleSourceCreatingARawModelMap extends RuleSource {
@Model
void bar(ModelMap foo) {
}
}
def "type of the first argument of void returning model definition has to be a valid managed type"() {
when:
extract(RuleSourceCreatingARawModelMap)
then:
InvalidModelRuleDeclarationException e = thrown()
e.message == """Type ${fullyQualifiedNameOf(RuleSourceCreatingARawModelMap)} is not a valid rule source:
- Method bar(org.gradle.model.ModelMap) is not a valid rule method: Raw type org.gradle.model.ModelMap used for parameter 1 (all type parameters must be specified of parameterized type)"""
}
static class RuleSourceWithAVoidReturningNoArgumentMethod extends RuleSource {
@Model
void bar() {
}
}
def "void returning model definition has to take at least one argument"() {
when:
extract(RuleSourceWithAVoidReturningNoArgumentMethod)
then:
InvalidModelRuleDeclarationException e = thrown()
e.message == """Type ${fullyQualifiedNameOf(RuleSourceWithAVoidReturningNoArgumentMethod)} is not a valid rule source:
- Method bar() is not a valid rule method: A method annotated with @Model must either take at least one parameter or have a non-void return type"""
}
static class RuleSourceCreatingManagedWithNestedPropertyOfInvalidManagedType extends RuleSource {
@Model
void bar(ManagedWithNestedPropertyOfInvalidManagedType foo) {
}
}
static class RuleSourceCreatingManagedWithNestedReferenceOfInvalidManagedType extends RuleSource {
@Model
void bar(ManagedWithNestedReferenceOfInvalidManagedType foo) {
}
}
@Unroll
def "void returning model definition with for a type with a nested property of invalid managed type - #inspected.simpleName"() {
when:
extract(inspected)
then:
InvalidModelRuleDeclarationException e = thrown()
e.message == "Declaration of model rule ModelRuleExtractorTest.$inspected.simpleName#bar($managedType.simpleName) is invalid."
e.cause instanceof InvalidManagedModelElementTypeException
e.cause.message == """Type $ModelMap.name> is not a valid model element type:
- type parameter of org.gradle.model.ModelMap cannot be a wildcard.
The type was analyzed due to the following dependencies:
${fullyQualifiedNameOf(managedType)}
\\--- property 'managedWithNestedInvalidManagedType' (${fullyQualifiedNameOf(nestedManagedType)})
\\--- property 'invalidManaged' ($ModelMap.name>)"""
where:
inspected | managedType | nestedManagedType
RuleSourceCreatingManagedWithNestedPropertyOfInvalidManagedType | ManagedWithNestedPropertyOfInvalidManagedType | ManagedWithPropertyOfInvalidManagedType
RuleSourceCreatingManagedWithNestedReferenceOfInvalidManagedType | ManagedWithNestedReferenceOfInvalidManagedType | ManagedWithReferenceOfInvalidManagedType
}
static class RuleSourceCreatingManagedWithNonManageableParent extends RuleSource {
@Model
void bar(ManagedWithNonManageableParents foo) {
}
}
def "error message produced when super type is not a manageable type indicates the original (sub) type"() {
when:
extract(RuleSourceCreatingManagedWithNonManageableParent)
then:
InvalidModelRuleDeclarationException e = thrown()
e.message == "Declaration of model rule ModelRuleExtractorTest.RuleSourceCreatingManagedWithNonManageableParent#bar(ManagedWithNonManageableParents) is invalid."
e.cause instanceof InvalidManagedModelElementTypeException
e.cause.message == """Type $ModelMap.name> is not a valid model element type:
- type parameter of org.gradle.model.ModelMap cannot be a wildcard.
The type was analyzed due to the following dependencies:
${fullyQualifiedNameOf(ManagedWithNonManageableParents)}
\\--- property 'invalidManaged' declared by ${fullyQualifiedNameOf(AnotherManagedWithPropertyOfInvalidManagedType)}, ${fullyQualifiedNameOf(ManagedWithPropertyOfInvalidManagedType)} ($ModelMap.name>)"""
where:
invalidTypeName = "$ParametrizedManaged.name<$String.name>"
}
static class HasRuleWithUncheckedModelMap extends RuleSource {
@Model
static ModelThing modelPath(ModelMap foo) {
new ModelThing("foo")
}
}
def "error when trying to use model map without specifying type param"() {
when:
extract(HasRuleWithUncheckedModelMap)
then:
InvalidModelRuleDeclarationException e = thrown()
e.message == """Type ${fullyQualifiedNameOf(HasRuleWithUncheckedModelMap)} is not a valid rule source:
- Method modelPath(org.gradle.model.ModelMap) is not a valid rule method: Raw type org.gradle.model.ModelMap used for parameter 1 (all type parameters must be specified of parameterized type)"""
}
static class NotEverythingAnnotated extends RuleSource {
void mutate(String thing) {}
private void ok() {}
}
def "all non-private methods must be annotated"() {
when:
extract(NotEverythingAnnotated)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(NotEverythingAnnotated)} is not a valid rule source:
- Method mutate(java.lang.String) is not a valid rule method: A method that is not annotated as a rule must be private"""
}
static class PrivateAnnotated extends RuleSource {
@Mutate
private void notOk(String subject) {}
}
def "no private methods may be annotated"() {
when:
extract(PrivateAnnotated)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == """Type ${fullyQualifiedNameOf(PrivateAnnotated)} is not a valid rule source:
- Method notOk(java.lang.String) is not a valid rule method: A rule method cannot be private"""
}
static class SeveralProblems {
private String field1
private String field2
@Mutate
private void notOk() {}
public void notARule() {}
@Mutate
@Validate
private String multipleProblems(@Path('') List list, @Path(':)') T value) {
"broken"
}
@Model(":)")
void thing() {
}
}
def "collects all validation problems"() {
when:
extract(SeveralProblems)
then:
def e = thrown(InvalidModelRuleDeclarationException)
e.message == '''Type org.gradle.model.internal.inspect.ModelRuleExtractorTest.SeveralProblems is not a valid rule source:
- Rule source classes must directly extend org.gradle.model.RuleSource
- Field field1 is not valid: Fields must be static final.
- Field field2 is not valid: Fields must be static final.
- Method multipleProblems(java.util.List, T) is not a valid rule method: Can only be one of [annotated with @Model and returning a model element, annotated with @Model and taking a managed model element, annotated with @Defaults, annotated with @Mutate, annotated with @Finalize, annotated with @Validate, annotated with @Rules]
- Method multipleProblems(java.util.List, T) is not a valid rule method: A rule method cannot be private
- Method multipleProblems(java.util.List, T) is not a valid rule method: Cannot have type variables (i.e. cannot be a generic method)
- Method multipleProblems(java.util.List, T) is not a valid rule method: Raw type java.util.List used for parameter 1 (all type parameters must be specified of parameterized type)
- Method multipleProblems(java.util.List, T) is not a valid rule method: The declared model element path '' used for parameter 1 is not a valid path: Cannot use an empty string as a model path.
- Method multipleProblems(java.util.List, T) is not a valid rule method: The declared model element path ':)' used for parameter 2 is not a valid path: Model element name ':)' has illegal first character ':' (names must start with an ASCII letter or underscore).
- Method notOk() is not a valid rule method: A rule method cannot be private
- Method notOk() is not a valid rule method: Cannot have type variables (i.e. cannot be a generic method)
- Method notOk() is not a valid rule method: A method annotated with @Mutate must have at least one parameter
- Method notARule() is not a valid rule method: A method that is not annotated as a rule must be private
- Method thing() is not a valid rule method: The declared model element path ':)' is not a valid path: Model element name ':)' has illegal first character ':' (names must start with an ASCII letter or underscore).
- Method thing() is not a valid rule method: A method annotated with @Model must either take at least one parameter or have a non-void return type'''
}
static class RuleSourceWithDependencies extends RuleSource {
@Mutate
void method1(Long l) { }
@Mutate
void method2(String s) { }
}
def "rule method can imply plugin dependency"() {
def ruleExtractor = Stub(MethodModelRuleExtractor)
def extractor = new ModelRuleExtractor([ruleExtractor], proxyFactory, schemaStore, structBindingsStore)
given:
ruleExtractor.isSatisfiedBy(_) >> { MethodRuleDefinition method -> method.isAnnotationPresent(Mutate) }
ruleExtractor.registration(_, _) >> { MethodRuleDefinition method, MethodModelRuleExtractionContext context ->
return Stub(ExtractedModelRule) {
getRuleDependencies() >> [method.getSubjectReference().getType().getConcreteClass()]
}
}
expect:
extractor.extract(RuleSourceWithDependencies).getRequiredPlugins() == [Long.class, String.class]
}
def "can assert no plugin dependencies"() {
def ruleExtractor = Stub(MethodModelRuleExtractor)
def extractor = new ModelRuleExtractor([ruleExtractor], proxyFactory, schemaStore, structBindingsStore)
given:
ruleExtractor.isSatisfiedBy(_) >> { MethodRuleDefinition method -> method.isAnnotationPresent(Mutate) }
ruleExtractor.registration(_, _) >> { MethodRuleDefinition method, MethodModelRuleExtractionContext context ->
return Stub(ExtractedModelRule) {
getDescriptor() >> method.getDescriptor()
getRuleDependencies() >> [method.getSubjectReference().getType().getConcreteClass()]
}
}
when:
extractor.extract(RuleSourceWithDependencies).assertNoPlugins()
then:
def e = thrown(UnsupportedOperationException)
e.message == "ModelRuleExtractorTest.RuleSourceWithDependencies#method1(Long) has dependencies on plugins: [class java.lang.Long]. Plugin dependencies are not supported in this context."
}
def "extracted stateless rules are cached"() {
when:
def fromFirstExtraction = extractor.extract(MutationRules)
def fromSecondExtraction = extractor.extract(MutationRules)
then:
fromFirstExtraction.is(fromSecondExtraction)
}
def "extracted stateless abstract rules are cached"() {
when:
def fromFirstExtraction = extractor.extract(AbstractRules)
def fromSecondExtraction = extractor.extract(AbstractRules)
then:
fromFirstExtraction.is(fromSecondExtraction)
}
def "new instance is created for extracted stateful abstract rules"() {
when:
def fromFirstExtraction = extractor.extract(AbstractPropertyRules)
def fromSecondExtraction = extractor.extract(AbstractPropertyRules)
then:
!fromFirstExtraction.is(fromSecondExtraction)
}
def "cache does not hold strong references"() {
given:
def cl = new GroovyClassLoader(getClass().classLoader)
def source = cl.parseClass('''
import org.gradle.model.*
class Rules extends RuleSource {
@Mutate
void mutate(String value) {
}
}
''')
when:
extractor.extract(source)
then:
extractor.cache.size() == 1
when:
cl.clearCache()
forcefullyClearReferences(source)
source = null
then:
ConcurrentTestUtil.poll(10) {
System.gc()
extractor.cache.cleanUp()
extractor.cache.size() == 0
}
}
static class InvalidEachAnnotation extends RuleSource {
@Mutate
void mutate(String value, @Each Integer input) {}
}
def "invalid @Each annotations are not allowed"() {
when:
extract InvalidEachAnnotation
then:
def e = thrown InvalidModelRuleDeclarationException
e.message == """Type ${fullyQualifiedNameOf(InvalidEachAnnotation)} is not a valid rule source:
- Method mutate(java.lang.String, java.lang.Integer) is not a valid rule method: Rule parameter #2 should not be annotated with @Each."""
}
static class InvalidEachAndPathAnnotation extends RuleSource {
@Mutate
void valid(@Path("value") String value, Integer input) {}
@Mutate
void invalid(@Each @Path("value") String value, Integer input) {}
}
def "both @Each and @Path annotations are not allowed"() {
when:
extract InvalidEachAndPathAnnotation
then:
def e = thrown InvalidModelRuleDeclarationException
e.message == """Type ${fullyQualifiedNameOf(InvalidEachAndPathAnnotation)} is not a valid rule source:
- Method invalid(java.lang.String, java.lang.Integer) is not a valid rule method: Rule subject must not be annotated with both @Path and @Each."""
}
private void forcefullyClearReferences(Class> clazz) {
ModelStoreTestUtils.removeClassFromGlobalClassSet(clazz)
// Remove soft references
Introspector.flushFromCaches(clazz)
}
MutableModelNode node() {
return Stub(MutableModelNode) {
getPath() >> ModelPath.ROOT
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy