org.gradle.api.internal.tasks.options.OptionReaderTest.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 2017 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.api.internal.tasks.options
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.internal.tasks.TaskOptionsGenerator
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.SetProperty
import org.gradle.api.tasks.options.Option
import org.gradle.api.tasks.options.OptionValues
import spock.lang.Issue
import spock.lang.Specification
class OptionReaderTest extends Specification {
OptionReader reader
int builtInOptionCount
def setup() {
reader = new OptionReader()
builtInOptionCount = TaskOptionsGenerator.BUILT_IN_OPTIONS.size();
}
def "can read options linked to setter methods of a task"() {
when:
List options = TaskOptionsGenerator.generate(new TestClassWithSetters(), reader).getAll()
then:
options[0].name == "aFlag"
options[0].description == "simple flag"
options[0].argumentType == Void.TYPE
options[0].availableValues == [] as Set
options[1].name == "no-aFlag"
options[1].description == "Disables option --aFlag."
options[1].argumentType == Void.TYPE
options[1].availableValues == [] as Set
options[2].name == "booleanValue"
options[2].description == "boolean value"
options[2].argumentType == Void.TYPE
options[2].availableValues == [] as Set
options[3].name == "no-booleanValue"
options[3].description == "Disables option --booleanValue."
options[3].argumentType == Void.TYPE
options[3].availableValues == [] as Set
options[4].name == "enumValue"
options[4].description == "enum value"
options[4].argumentType == String
options[4].availableValues == ["ABC", "DEF"] as Set
options[5].name == "integerValue"
options[5].description == "integer value"
options[5].argumentType == String
options[5].availableValues == [] as Set
options[6].name == "multiEnum"
options[6].description == "a list of enums"
options[6].argumentType == List
options[6].availableValues == [] as Set
options[7].name == "multiInteger"
options[7].description == "a list of integers"
options[7].argumentType == List
options[7].availableValues == [] as Set
options[8].name == "multiString"
options[8].description == "a list of strings"
options[8].argumentType == List
options[8].availableValues == [] as Set
options[9].name == "objectValue"
options[9].description == "object value"
options[9].argumentType == String
options[9].availableValues == [] as Set
options[10].name == "stringValue"
options[10].description == "string value"
options[10].argumentType == String
options[10].availableValues == ["dynValue1", "dynValue2"] as Set
}
def "can read options linked to property getter methods of a task"() {
when:
List options = TaskOptionsGenerator.generate(new TestClassWithProperties(), reader).getAll()
then:
options[0].name == "booleanValue"
options[0].description == "boolean value"
options[0].argumentType == Void.TYPE
options[0].availableValues == [] as Set
options[1].name == "no-booleanValue"
options[1].description == "Disables option --booleanValue."
options[1].argumentType == Void.TYPE
options[1].availableValues == [] as Set
options[2].name == "directoryValue"
options[2].description == "Directory value"
options[2].argumentType == String
options[3].name == "enumValue"
options[3].description == "enum value"
options[3].argumentType == String
options[3].availableValues == ["ABC", "DEF"] as Set
options[4].name == "integerValue"
options[4].description == "integer value"
options[4].argumentType == String
options[4].availableValues == [] as Set
options[5].name == "listEnumValue"
options[5].description == "list enum value"
options[5].argumentType == List
options[6].name == "listIntegerValue"
options[6].description == "list integer value"
options[6].argumentType == List
options[7].name == "listStringValue"
options[7].description == "list string value"
options[7].argumentType == List
options[8].name == "objectValue"
options[8].description == "object value"
options[8].argumentType == String
options[8].availableValues == [] as Set
options[9].name == "regularFileValue"
options[9].description == "RegularFile value"
options[9].argumentType == String
options[10].name == "setEnumValue"
options[10].description == "set enum value"
options[10].argumentType == List
options[11].name == "setIntegerValue"
options[11].description == "set integer value"
options[11].argumentType == List
options[12].name == "setStringValue"
options[12].description == "set string value"
options[12].argumentType == List
options[13].name == "stringValue"
options[13].description == "string value"
options[13].argumentType == String
options[13].availableValues == ["dynValue1", "dynValue2"] as Set
}
def "built-in options appear last"() {
when:
List options = TaskOptionsGenerator.generate(new TestClassWithProperties(), reader).getAll()
int ownOptions = 14
then:
options.forEach {it -> System.out.println(it.name + " " + it.description)}
TaskOptionsGenerator.BUILT_IN_OPTIONS.eachWithIndex { BuiltInOptionElement optionElement, int i ->
assert options[ownOptions + i].name == optionElement.optionName
assert options[ownOptions + i].description == optionElement.description
assert options[ownOptions + i].argumentType == Void.TYPE
assert options[ownOptions + i].availableValues == [] as Set
}
}
def "task own options shadow built-in options"() {
when:
List options = TaskOptionsGenerator.generate(new TestClassWithOptionNameClashing(), reader).getAll()
int ownOptions = 2
List clashingOptions = ["rerun"]
then:
options.size() == ownOptions + builtInOptionCount - clashingOptions.size()
options[0].name == "rerun"
options[0].description == "custom clashing option"
options[1].name == "unique"
options[1].description == "custom unique option"
}
def "task own options shadow generated opposite options"() {
when:
List options = TaskOptionsGenerator.generate(new TestClassWithOppositeOptionNameClashing(), reader).getAll()
int ownOptions = 2
then:
options.size() == ownOptions + builtInOptionCount
options[0].name == "my-option"
options[0].description == "Option to trigger creation of opposite option"
options[1].name == "no-my-option"
options[1].description == "Option clashing with opposite option"
options[2].name == "rerun"
options[2].description == "Causes the task to be re-run even if up-to-date."
}
def "task only opposite option"() {
when:
List options = TaskOptionsGenerator.generate(new TestClassWithOnlyOppositeOption(), reader).getAll()
int ownOptions = 4
then:
options.size() == ownOptions + builtInOptionCount
options[0].name == "my-option1"
options[0].description == "Opposite option of --no-my-option1."
options[1].name == "no-my-option1"
options[1].description == "Opposite option Boolean"
options[2].name == "my-option2"
options[2].description == "Opposite option of --no-my-option2."
options[3].name == "no-my-option2"
options[3].description == "Opposite option Property"
options[4].name == "rerun"
options[4].description == "Causes the task to be re-run even if up-to-date."
}
def "fail when multiple methods define same option"() {
when:
TaskOptionsGenerator.generate(new TestClass2(), reader).getAll()
then:
def e = thrown(OptionValidationException)
e.message == "@Option 'stringValue' linked to multiple elements in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass2'."
}
def "fail when multiple methods from different types define same option"() {
when:
TaskOptionsGenerator.generate(new WithDuplicateOptionInAnotherInterface(), reader).getAll()
then:
def e = thrown(OptionValidationException)
e.message == "@Option 'stringValue' linked to multiple elements in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$WithDuplicateOptionInAnotherInterface'."
}
def "fails on static methods"() {
when:
TaskOptionsGenerator.generate(new TestClass31(), reader).getAll()
then:
def e = thrown(OptionValidationException)
e.message == "@Option on static method 'setStaticString' not supported in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass31'."
}
def "fails on static fields"() {
when:
TaskOptionsGenerator.generate(new TestClass32(), reader).getAll()
then:
def e = thrown(OptionValidationException)
e.message == "@Option on static field 'staticField' not supported in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass32'."
}
def "fail when parameter cannot be converted from the command-line"() {
when:
TaskOptionsGenerator.generate(new TestClass5(), reader).getAll()
then:
def e = thrown(OptionValidationException)
e.message == "Option 'fileValue' cannot be cast to type 'java.io.File' in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass5'."
}
def "fails when method has > 1 parameter"() {
when:
TaskOptionsGenerator.generate(new TestClass4(), reader).getAll()
then:
def e = thrown(OptionValidationException)
e.message == "Option 'stringValue' on method cannot take multiple parameters in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass4#setStrings'."
}
def "handles field options"() {
when:
List options = TaskOptionsGenerator.generate(new TestClassWithFields(), reader).getAll()
then:
options[0].name == "customOptionName"
options[0].description == "custom description"
options[0].argumentType == String
options[1].name == "field2"
options[1].description == "Descr Field2"
options[1].argumentType == String
options[1].availableValues == ["dynValue1", "dynValue2"] as Set
options[2].name == "field3"
options[2].description == "Descr Field3"
options[2].argumentType == String
options[2].availableValues as Set == ["ABC", "DEF"] as Set
options[3].name == "field4"
options[3].description == "Descr Field4"
options[3].argumentType == Void.TYPE
options[3].availableValues.isEmpty()
options[4].name == "no-field4"
options[4].description == "Disables option --field4."
options[4].argumentType == Void.TYPE
options[4].availableValues.isEmpty()
options[5].name == "field5"
options[5].description == "Descr Field5"
options[5].argumentType == String
options[5].availableValues.isEmpty()
options[6].name == "field6"
options[6].description == "Descr Field6"
options[6].argumentType == List
options[6].availableValues.isEmpty()
options[7].name == "field7"
options[7].description == "Descr Field7"
options[7].argumentType == List
options[7].availableValues.isEmpty()
options[8].name == "field8"
options[8].description == "Descr Field8"
options[8].argumentType == List
options[8].availableValues.isEmpty()
}
def "handles property field options"() {
when:
List options = TaskOptionsGenerator.generate(new TestClassWithPropertyField(), reader).getAll()
then:
options[0].name == "booleanValue"
options[0].description == "Descr booleanValue"
options[0].argumentType == Void.TYPE
options[0].availableValues.isEmpty()
options[1].name == "no-booleanValue"
options[1].description == "Disables option --booleanValue."
options[1].argumentType == Void.TYPE
options[1].availableValues.isEmpty()
options[2].name == "customOptionName"
options[2].description == "custom description"
options[2].argumentType == String
options[3].name == "directoryValue"
options[3].description == "Descr directoryValue"
options[3].argumentType == String
options[4].name == "enumListValue"
options[4].description == "Descr enumListValue"
options[4].argumentType == List
options[5].name == "enumSetValue"
options[5].description == "Descr enumSetValue"
options[5].argumentType == List
options[6].name == "enumValue"
options[6].description == "Descr enumValue"
options[6].argumentType == String
options[6].availableValues as Set == ["ABC", "DEF"] as Set
options[7].name == "integerListValue"
options[7].description == "Descr integerListValue"
options[7].argumentType == List
options[8].name == "integerSetValue"
options[8].description == "Descr integerSetValue"
options[8].argumentType == List
options[9].name == "integerValue"
options[9].description == "Descr integerValue"
options[9].argumentType == String
options[10].name == "regularFileValue"
options[10].description == "Descr regularFileValue"
options[10].argumentType == String
options[11].name == "stringListValue"
options[11].description == "Descr stringListValue"
options[11].argumentType == List
options[12].name == "stringSetValue"
options[12].description == "Descr stringSetValue"
options[12].argumentType == List
options[13].name == "stringValue"
options[13].description == "Descr stringValue"
options[13].argumentType == String
options[13].availableValues == ["dynValue1", "dynValue2"] as Set
}
def "throws decent error when description not set"() {
when:
TaskOptionsGenerator.generate(new TestClass7(), reader).getAll()
then:
def e = thrown(OptionValidationException)
e.message == "No description set on option 'aValue' at for class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass7'."
when:
TaskOptionsGenerator.generate(new TestClass8(), reader).getAll()
then:
e = thrown(OptionValidationException)
e.message == "No description set on option 'field' at for class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass8'."
}
def "throws decent error when method annotated without option name set"() {
when:
TaskOptionsGenerator.generate(new TestClass9(), reader).getAll()
then:
def e = thrown(OptionValidationException)
e.message == "No option name set on 'setStrings' in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass9'."
}
def "throws decent error when private field is annotated as option and no setter declared"() {
when:
TaskOptionsGenerator.generate(new TestClass10(), reader).getAll()
then:
def e = thrown(OptionValidationException)
e.message == "No setter for Option annotated field 'field' in class 'class org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass10'."
}
def "throws decent error for invalid OptionValues annotated methods"() {
when:
TaskOptionsGenerator.generate(new WithInvalidSomeOptionMethod(), reader).getAll()
then:
def e = thrown(OptionValidationException)
e.message == "@OptionValues annotation not supported on method 'getValues' in class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$WithInvalidSomeOptionMethod'. Supported method must be non-static, return a Collection and take no parameters.";
when:
TaskOptionsGenerator.generate(new TestClass8(), reader).getAll()
then:
e = thrown(OptionValidationException)
e.message == "No description set on option 'field' at for class 'org.gradle.api.internal.tasks.options.OptionReaderTest\$TestClass8'."
}
@Issue("https://github.com/gradle/gradle/issues/18496")
def "handles abstract classes with interfaces"() {
when:
List options = TaskOptionsGenerator.generate((new AbstractTestClassWithInterface() {
@Override
Property getStringValue() {
throw new UnsupportedOperationException()
}
}), reader).getAll()
then:
options.size() == 2 + builtInOptionCount
options[0].name == "objectValue"
options[0].description == "object value"
options[0].argumentType == String
options[0].availableValues == [] as Set
options[1].name == "stringValue"
options[1].description == "string value"
options[1].argumentType == String
options[1].availableValues == [] as Set
}
@Issue("https://github.com/gradle/gradle/issues/18496")
def "handles abstract classes with interfaces with same method but different option names"() {
when:
List options = TaskOptionsGenerator.generate((new AbstractTestClassWithTwoInterfacesWithSameMethod() {
@Override
Property getStringValue() {
throw new UnsupportedOperationException()
}
}), reader).getAll()
then:
options.size() == 3 + builtInOptionCount
options[0].name == "objectValue"
options[0].description == "object value"
options[0].argumentType == String
options[0].availableValues == [] as Set
options[1].name == "stringValue"
options[1].description == "string value"
options[1].argumentType == String
options[1].availableValues == [] as Set
options[2].name == "stringValue1"
options[2].description == "string value 1"
options[2].argumentType == String
options[2].availableValues == [] as Set
}
def "class that defines option when parent class and interface do has uses the sub-class option"() {
when:
List options = TaskOptionsGenerator.generate(new OverrideCheckSubClassDefinesOption(), reader).getAll()
then:
options.size() == 1 + builtInOptionCount
options[0].name == "base"
options[0].description == "from sub class"
}
def "class that has an option defined in parent class and interface uses the parent class option"() {
when:
List options = TaskOptionsGenerator.generate(new OverrideCheckSubClassSaysNothing(), reader).getAll()
then:
options.size() == 1 + builtInOptionCount
options[0].name == "base"
options[0].description == "from base class"
}
def "class that defines option when parent class which impls interface do has uses the sub-class option"() {
when:
List options = TaskOptionsGenerator.generate(new OverrideCheckSubClassImplInterfaceDefinesOption(), reader).getAll()
then:
options.size() == 1 + builtInOptionCount
options[0].name == "base"
options[0].description == "from sub class"
}
def "class that has an option defined in parent class which impls interface uses the parent class option"() {
when:
List options = TaskOptionsGenerator.generate(new OverrideCheckSubClassImplInterfaceSaysNothing(), reader).getAll()
then:
options.size() == 1 + builtInOptionCount
options[0].name == "base"
options[0].description == "from base class"
}
public interface TestInterface {
@Option(option = "stringValue", description = "string value")
public Property getStringValue();
}
public interface TestInterfaceWithSameFunctionButDifferentOptionName {
@Option(option = "stringValue1", description = "string value 1")
public Property getStringValue();
}
public static abstract class AbstractTestClassWithInterface implements TestInterface {
@Option(option = "objectValue", description = "object value")
public void setObjectValue(Object value) {
}
}
public static abstract class AbstractTestClassWithTwoInterfacesWithSameMethod implements TestInterface, TestInterfaceWithSameFunctionButDifferentOptionName {
@Option(option = "objectValue", description = "object value")
public void setObjectValue(Object value) {
}
}
// These demonstrate override behavior
static class OverrideCheckBaseClass {
@Option(option = "base", description = "from base class")
public void setBase(String value) {
}
}
interface OverrideCheckBaseInterface {
@Option(option = "base", description = "from base interface")
public void setBase(String value);
}
static class OverrideCheckSubClassDefinesOption extends OverrideCheckBaseClass implements OverrideCheckBaseInterface {
@Option(option = "base", description = "from sub class")
public void setBase(String value) {
}
}
static class OverrideCheckSubClassSaysNothing extends OverrideCheckBaseClass implements OverrideCheckBaseInterface {
}
static class OverrideCheckBaseClassImplInterface implements OverrideCheckBaseInterface {
@Option(option = "base", description = "from base class")
public void setBase(String value) {
}
}
static class OverrideCheckSubClassImplInterfaceDefinesOption extends OverrideCheckBaseClassImplInterface {
@Option(option = "base", description = "from sub class")
public void setBase(String value) {
}
}
static class OverrideCheckSubClassImplInterfaceSaysNothing extends OverrideCheckBaseClassImplInterface {
}
public static class TestClassWithSetters {
@Option(option = "stringValue", description = "string value")
public void setStringValue(String value) {
}
@Option(option = "objectValue", description = "object value")
public void setObjectValue(Object value) {
}
@Option(option = "booleanValue", description = "boolean value")
public void setBooleanValue(boolean value) {
}
@Option(option = "enumValue", description = "enum value")
public void setEnumValue(TestEnum value) {
}
@Option(option = "integerValue", description = "integer value")
public void setIntegerValue(Integer value) {
}
@Option(option = "aFlag", description = "simple flag")
public void setActive() {
}
@Option(option = "multiString", description = "a list of strings")
public void setStringListValue(List values) {
}
@Option(option = "multiEnum", description = "a list of enums")
public void setEnumListValue(List values) {
}
@Option(option = "multiInteger", description = "a list of integers")
public void setIntegerListValue(List values) {
}
@OptionValues("stringValue")
public Collection getAvailableValues() {
return Arrays.asList(new CustomClass(value: "dynValue1"), new CustomClass(value: "dynValue2"))
}
}
public static class TestClassWithProperties {
@Option(option = "booleanValue", description = "boolean value")
public Property getBooleanValue() {
throw new UnsupportedOperationException()
}
@Option(option = "directoryValue", description = "Directory value")
public DirectoryProperty getDirectoryValue() {
throw new UnsupportedOperationException()
}
@Option(option = "enumValue", description = "enum value")
public Property getEnumValue() {
throw new UnsupportedOperationException()
}
@Option(option = "integerValue", description = "integer value")
public Property getIntegerValue() {
throw new UnsupportedOperationException()
}
@Option(option = "listEnumValue", description = "list enum value")
public ListProperty getListEnumValue() {
throw new UnsupportedOperationException()
}
@Option(option = "listIntegerValue", description = "list integer value")
public ListProperty getListIntegerValue() {
throw new UnsupportedOperationException()
}
@Option(option = "listStringValue", description = "list string value")
public ListProperty getListStringValue() {
throw new UnsupportedOperationException()
}
@Option(option = "regularFileValue", description = "RegularFile value")
public RegularFileProperty getRegularFileValue() {
throw new UnsupportedOperationException()
}
@Option(option = "setEnumValue", description = "set enum value")
public SetProperty getSetEnumValue() {
throw new UnsupportedOperationException()
}
@Option(option = "setIntegerValue", description = "set integer value")
public SetProperty getSetIntegerValue() {
throw new UnsupportedOperationException()
}
@Option(option = "setStringValue", description = "set string value")
public SetProperty getSetStringValue() {
throw new UnsupportedOperationException()
}
@Option(option = "stringValue", description = "string value")
public Property getStringValue() {
throw new UnsupportedOperationException()
}
@Option(option = "objectValue", description = "object value")
public Property
© 2015 - 2025 Weber Informatics LLC | Privacy Policy