All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.gradle.api.internal.provider.CollectionPropertySpec.groovy Maven / Gradle / Ivy

There is a newer version: 8.11.1
Show newest version
/*
 * 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.provider

import com.google.common.collect.ImmutableCollection
import org.gradle.api.Transformer
import org.gradle.api.provider.Provider
import spock.lang.Unroll

abstract class CollectionPropertySpec> extends PropertySpec {
    @Override
    AbstractCollectionProperty propertyWithDefaultValue() {
        return property()
    }

    @Override
    AbstractCollectionProperty propertyWithNoValue() {
        def p = property()
        p.set((Iterable) null)
        return p
    }

    @Override
    AbstractCollectionProperty providerWithValue(C value) {
        def p = property()
        p.set(value)
        return p
    }

    @Override
    C someValue() {
        return toMutable(["s1", "s2"])
    }

    @Override
    C someOtherValue() {
        return toMutable(["s1"])
    }

    abstract AbstractCollectionProperty property()

    @Override
    abstract Class type()

    @Override
    protected void setToNull(Object property) {
        property.set((Iterable) null)
    }

    def property = property()

    protected void assertValueIs(Collection expected) {
        assert property.present
        def actual = property.get()
        assert actual instanceof ImmutableCollection
        assert immutableCollectionType.isInstance(actual)
        assert actual == toImmutable(expected)
        actual.each {
            assert it instanceof String
        }
        assert property.present
    }

    protected abstract C toImmutable(Collection values)

    protected abstract C toMutable(Collection values)

    protected abstract Class> getImmutableCollectionType()

    def "has empty collection as value by default"() {
        expect:
        assertValueIs([])
    }

    def "can change value to empty collection"() {
        property.empty()

        expect:
        assertValueIs([])
    }

    def "can set value using empty collection"() {
        expect:
        property.set(toMutable([]))
        assertValueIs([])
    }

    def "returns immutable copy of value"() {
        expect:
        property.set(toMutable(["abc"]))
        assertValueIs(["abc"])
    }

    def "can set value from various collection types"() {
        def iterable = Stub(Iterable)
        iterable.iterator() >> ["4", "5"].iterator()

        expect:
        property.set(["1", "2"])
        property.get() == toImmutable(["1", "2"])

        property.set(["2", "3"] as Set)
        property.get() == toImmutable(["2", "3"])

        property.set(iterable)
        property.get() == toImmutable(["4", "5"])
    }

    def "can set string property from collection containing GString"() {
        expect:
        property.set(["${'321'.substring(2)}"])
        assertValueIs(["1"])
    }

    def "can set untyped from various collection types"() {
        def iterable = Stub(Iterable)
        iterable.iterator() >> ["4", "5"].iterator()

        expect:
        property.setFromAnyValue(["1", "2"])
        property.get() == toImmutable(["1", "2"])

        property.setFromAnyValue(["2", "3"] as Set)
        property.get() == toImmutable(["2", "3"])

        property.setFromAnyValue(iterable)
        property.get() == toImmutable(["4", "5"])
    }

    def "can set untyped from provider"() {
        def provider = Stub(ProviderInternal)
        provider.type >> null
        provider.get() >>> [["1"], ["2"]]

        expect:
        property.setFromAnyValue(provider)
        property.get() == toImmutable(["1"])
        property.get() == toImmutable(["2"])
    }

    def "can set string property from provider that returns collection containing GString"() {
        def provider = Stub(Provider)
        def value = ["${'321'.substring(2)}"]
        provider.get() >>> value

        expect:
        property.set(value)
        assertValueIs(["1"])
    }

    def "queries initial value for every call to get()"() {
        expect:
        def initialValue = toMutable(["abc"])
        property.set(initialValue)
        assertValueIs(["abc"])
        initialValue.add("added")
        assertValueIs(["abc", "added"])
    }

    def "queries underlying provider for every call to get()"() {
        def provider = Stub(ProviderInternal)
        provider.type >> List
        provider.get() >>> [["123"], ["abc"]]
        provider.present >> true

        expect:
        property.set(provider)
        assertValueIs(["123"])
        assertValueIs(["abc"])
    }

    def "mapped provider is presented with immutable copy of value"() {
        given:
        property.set(toMutable(["abc"]))
        def provider = property.map(new Transformer() {
            def transform(def value) {
                assert immutableCollectionType.isInstance(value)
                assert value == toImmutable(["abc"])
                return toMutable(["123"])
            }
        })

        expect:
        def actual = provider.get()
        actual == toMutable(["123"])
    }

    def "appends a single value using add"() {
        given:
        property.set(toMutable(["abc"]))
        property.add("123")
        property.add("456")

        expect:
        assertValueIs(["abc", "123", "456"])
    }

    def "appends a single value to string property using GString"() {
        given:
        property.set(toMutable(["abc"]))
        property.add("${'321'.substring(2)}")

        expect:
        assertValueIs(["abc", "1"])
    }

    def "appends a single value from provider using add"() {
        given:
        property.set(toMutable(["abc"]))
        property.add(Providers.of("123"))
        property.add(Providers.of("456"))

        expect:
        assertValueIs(["abc", "123", "456"])
    }

    def "appends a single value to string property from provider with GString value using add"() {
        given:
        property.set(toMutable(["abc"]))
        property.add(Providers.of("${'321'.substring(2)}"))

        expect:
        assertValueIs(["abc", "1"])
    }

    @Unroll
    def "appends zero or more values from array #value using addAll"() {
        given:
        property.addAll(value as String[])

        expect:
        assertValueIs(expectedValue)

        where:
        value                 | expectedValue
        []                    | []
        ["aaa"]               | ["aaa"]
        ["aaa", "bbb", "ccc"] | ["aaa", "bbb", "ccc"]
    }

    def "appends value to string property from array with GString value using addAll"() {
        given:
        property.set(toMutable(["abc"]))
        property.addAll("${'321'.substring(2)}")

        expect:
        assertValueIs(["abc", "1"])
    }

    @Unroll
    def "appends zero or more values from provider #value using addAll"() {
        given:
        property.addAll(Providers.of(value))

        expect:
        assertValueIs(expectedValue)

        where:
        value                 | expectedValue
        []                    | []
        ["aaa"]               | ["aaa"]
        ["aaa", "bbb", "ccc"] | ["aaa", "bbb", "ccc"]
    }

    def "queries values of provider on every call to get()"() {
        def provider = Stub(ProviderInternal)
        _ * provider.present >> true
        _ * provider.get() >>> [["abc"], ["def"]]

        expect:
        property.addAll(provider)
        assertValueIs(["abc"])
        assertValueIs(["def"])
    }

    def "appends value to string property from provider with GString value using addAll"() {
        given:
        property.set(toMutable(["abc"]))
        property.addAll(Providers.of(["${'321'.substring(2)}"]))

        expect:
        assertValueIs(["abc", "1"])
    }

    @Unroll
    def "appends zero or more values from collection #value using addAll"() {
        given:
        property.addAll(value)

        expect:
        assertValueIs(expectedValue)

        where:
        value                 | expectedValue
        []                    | []
        ["aaa"]               | ["aaa"]
        ["aaa", "bbb", "ccc"] | ["aaa", "bbb", "ccc"]
    }

    def "queries values of collection on every call to get()"() {
        expect:
        def value = ["abc"]
        property.addAll(value)
        assertValueIs(["abc"])
        value.add("added")
        assertValueIs(["abc", "added"])
    }

    def "appends value to string property from collection with GString value using addAll"() {
        given:
        property.set(toMutable(["abc"]))
        property.addAll(["${'321'.substring(2)}"])

        expect:
        assertValueIs(["abc", "1"])
    }

    def "providers only called once per query"() {
        def valueProvider = Mock(ProviderInternal)
        def addProvider = Mock(ProviderInternal)
        def addAllProvider = Mock(ProviderInternal)

        given:
        property.set(valueProvider)
        property.add(addProvider)
        property.addAll(addAllProvider)

        when:
        property.present

        then:
        1 * valueProvider.present >> true
        1 * addProvider.present >> true
        1 * addAllProvider.present >> true
        0 * _

        when:
        property.get()

        then:
        1 * valueProvider.get() >> ["1"]
        1 * addProvider.get() >> "2"
        1 * addAllProvider.get() >> ["3"]
        0 * _

        when:
        property.getOrNull()

        then:
        1 * valueProvider.getOrNull() >> ["1"]
        1 * addProvider.getOrNull() >> "2"
        1 * addAllProvider.getOrNull() >> ["3"]
        0 * _
    }

    def "can append values to empty property"() {
        given:
        property.add("1")
        property.add(Providers.of("2"))
        property.addAll(["3"])
        property.addAll(Providers.of(["4"]))

        expect:
        assertValueIs(["1", "2", "3", "4"])
    }

    def "empty collection is used as value when elements added after convention set"() {
        given:
        property.convention(["1", "2"])
        property.add("3")

        expect:
        assertValueIs(["3"])
    }

    def "property has no value when set to null and other values appended"() {
        given:
        property.set((Iterable) null)
        property.add("1")
        property.add(Providers.of("2"))
        property.addAll(["3"])
        property.addAll(Providers.of(["4"]))

        expect:
        !property.present
        property.getOrNull() == null
        property.getOrElse(toMutable(["other"])) == toMutable(["other"])

        when:
        property.get()

        then:
        def e = thrown(IllegalStateException)
        e.message == Providers.NULL_VALUE
    }

    def "property has no value when set to provider with no value and other values appended"() {
        given:
        property.set(Providers.notDefined())

        and:
        property.add("1")
        property.add(Providers.of("2"))
        property.addAll(["3"])
        property.addAll(Providers.of(["4"]))

        expect:
        !property.present
        property.getOrNull() == null
        property.getOrElse(toMutable(["other"])) == toMutable(["other"])

        when:
        property.get()

        then:
        def e = thrown(IllegalStateException)
        e.message == Providers.NULL_VALUE
    }

    def "property has no value when adding an element provider with no value"() {
        given:
        property.set(toMutable(["123"]))
        property.add("456")
        property.add(Providers.notDefined())

        expect:
        !property.present
        property.getOrNull() == null
        property.getOrElse(toMutable(["other"])) == toMutable(["other"])

        when:
        property.get()

        then:
        def e = thrown(IllegalStateException)
        e.message == Providers.NULL_VALUE
    }

    def "property has no value when adding an collection provider with no value"() {
        given:
        property.set(toMutable(["123"]))
        property.add("456")
        property.addAll(Providers.notDefined())

        expect:
        !property.present
        property.getOrNull() == null
        property.getOrElse(toMutable(["other"])) == toMutable(["other"])

        when:
        property.get()

        then:
        def e = thrown(IllegalStateException)
        e.message == Providers.NULL_VALUE
    }

    def "can set to null value to discard value"() {
        given:
        def property = property()
        property.set(someValue())
        property.set((Iterable) null)

        expect:
        !property.present
        property.getOrNull() == null
        property.getOrElse(someValue()) == someValue()
        property.getOrElse(null) == null
    }

    def "can set null value to remove any added values"() {
        property.add("abc")
        property.add(Providers.of("def"))
        property.addAll(Providers.of(["hij"]))

        property.set((Iterable) null)

        expect:
        !property.present
        property.getOrNull() == null
        property.getOrElse(someValue()) == someValue()
        property.getOrElse(null) == null
    }

    def "can set value to replace added values"() {
        property.add("abc")
        property.add(Providers.of("def"))
        property.addAll("ghi")
        property.addAll(["jkl"])
        property.addAll(Providers.of(["mno"]))

        expect:
        property.set(toMutable(["123", "456"]))
        assertValueIs(["123", "456"])
    }

    def "can make empty to replace added values"() {
        property.add("abc")
        property.add(Providers.of("def"))
        property.addAll("ghi")
        property.addAll(["jkl"])
        property.addAll(Providers.of(["mno"]))

        expect:
        property.empty()
        assertValueIs([])
    }

    def "throws NullPointerException when provider returns list with null to property"() {
        given:
        property.addAll(Providers.of([null]))

        when:
        property.get()

        then:
        def ex = thrown(NullPointerException)
    }

    def "throws NullPointerException when adding a null value to the property"() {
        when:
        property.add(null)

        then:
        def ex = thrown(NullPointerException)
        ex.message == "Cannot add a null element to a property of type ${type().simpleName}."
    }

    def "ignores convention after element added"() {
        expect:
        property.add("a")
        property.convention(["other"])
        assertValueIs(["a"])
    }

    def "ignores convention after element added using provider"() {
        expect:
        property.add(Providers.of("a"))
        property.convention(["other"])
        assertValueIs(["a"])
    }

    def "ignores convention after elements added"() {
        expect:
        property.addAll(["a", "b"])
        property.convention(["other"])
        assertValueIs(["a", "b"])
    }

    def "ignores convention after elements added using provider"() {
        expect:
        property.addAll(Providers.of(["a", "b"]))
        property.convention(["other"])
        assertValueIs(["a", "b"])
    }

    def "ignores convention after collection made empty"() {
        expect:
        property.empty()
        property.convention(["other"])
        assertValueIs([])
    }

    def "cannot set to empty list after value finalized"() {
        given:
        def property = property()
        property.set(someValue())
        property.finalizeValue()

        when:
        property.empty()

        then:
        def e = thrown(IllegalStateException)
        e.message == 'The value for this property is final and cannot be changed any further.'
    }

    def "ignores set to empty list after value finalized leniently"() {
        given:
        def property = property()
        property.set(someValue())
        property.finalizeValueOnReadAndWarnAboutChanges()
        property.get()

        when:
        property.empty()

        then:
        property.get() == toImmutable(someValue())
    }

    def "cannot add element after value finalized"() {
        given:
        def property = property()
        property.set(someValue())
        property.finalizeValue()

        when:
        property.add("123")

        then:
        def e = thrown(IllegalStateException)
        e.message == 'The value for this property is final and cannot be changed any further.'

        when:
        property.add(Stub(PropertyInternal))

        then:
        def e2 = thrown(IllegalStateException)
        e2.message == 'The value for this property is final and cannot be changed any further.'
    }

    def "ignores add element after value finalized leniently"() {
        given:
        def property = property()
        property.set(someValue())
        property.finalizeValueOnReadAndWarnAboutChanges()
        property.get()

        when:
        property.add("123")
        property.add(Stub(PropertyInternal))

        then:
        property.get() == toImmutable(someValue())
    }

    def "cannot add elements after value finalized"() {
        given:
        def property = property()
        property.set(someValue())
        property.finalizeValue()

        when:
        property.addAll("123", "456")

        then:
        def e = thrown(IllegalStateException)
        e.message == 'The value for this property is final and cannot be changed any further.'

        when:
        property.addAll(["123", "456"])

        then:
        def e2 = thrown(IllegalStateException)
        e2.message == 'The value for this property is final and cannot be changed any further.'

        when:
        property.addAll(Stub(ProviderInternal))

        then:
        def e3 = thrown(IllegalStateException)
        e3.message == 'The value for this property is final and cannot be changed any further.'
    }

    def "ignores add elements after value finalized leniently"() {
        given:
        def property = property()
        property.set(someValue())
        property.finalizeValueOnReadAndWarnAboutChanges()
        property.get()

        when:
        property.addAll("123", "456")
        property.addAll(["123", "456"])
        property.addAll(Stub(ProviderInternal))

        then:
        property.get() == toImmutable(someValue())
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy