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

org.gradle.integtests.resolve.api.ResolutionResultApiIntegrationTest.groovy Maven / Gradle / Ivy

There is a newer version: 8.6
Show newest version
/*
 * Copyright 2018 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.integtests.resolve.api

import org.gradle.integtests.fixtures.AbstractDependencyResolutionTest
import org.gradle.integtests.fixtures.FeaturePreviewsFixture
import org.gradle.integtests.fixtures.FluidDependenciesResolveRunner
import org.gradle.integtests.fixtures.resolve.ResolveTestFixture
import org.junit.runner.RunWith
import spock.lang.Unroll

@RunWith(FluidDependenciesResolveRunner)
class ResolutionResultApiIntegrationTest extends AbstractDependencyResolutionTest {
    ResolveTestFixture resolve = new ResolveTestFixture(buildFile, 'conf')

    /*
    The ResolutionResult API is also covered by the dependency report integration tests.
     */

    def "selection reasons are described"() {
        given:
        mavenRepo.module("org", "leaf", "1.0").publish()
        mavenRepo.module("org", "leaf", "2.0").publish()
        mavenRepo.module("org", "foo", "0.5").publish()

        mavenRepo.module("org", "foo", "1.0").dependsOn('org', 'leaf', '1.0').publish()
        mavenRepo.module("org", "bar", "1.0").dependsOn('org', 'leaf', '2.0').publish()
        mavenRepo.module("org", "baz", "1.0").dependsOn('org', 'foo',  '1.0').publish()

        file("settings.gradle") << "rootProject.name = 'cool-project'"

        file("build.gradle") << """
            version = '5.0'
            repositories {
                maven { url "${mavenRepo.uri}" }
            }
            configurations {
                conf
            }
            configurations.conf.resolutionStrategy.force 'org:leaf:2.0'
            dependencies {
                conf 'org:foo:0.5', 'org:bar:1.0', 'org:baz:1.0'
            }
            task resolutionResult {
                doLast {
                    def result = configurations.conf.incoming.resolutionResult
                    result.allComponents {
                        if(it.id instanceof ModuleComponentIdentifier) {
                            println it.id.module + ":" + it.id.version + " " + it.selectionReason.description
                        }
                        else if(it.id instanceof ProjectComponentIdentifier) {
                            println it.moduleVersion.name + ":" + it.moduleVersion.version + " " + it.selectionReason.description
                        }
                    }
                }
            }
        """

        when:
        run "resolutionResult"

        then:
        output.contains """
cool-project:5.0 root
foo:1.0 between versions 1.0 and 0.5
leaf:2.0 forced
bar:1.0 requested
baz:1.0 requested
"""
    }

    def "resolution result API gives access to dependency reasons in case of conflict"() {
        given:
        mavenRepo.with {
            def leaf1 = module('org.test', 'leaf', '1.0').publish()
            def leaf2 = module('org.test', 'leaf', '1.1').publish()
            module('org.test', 'a', '1.0')
                .dependsOn(leaf1, reason: 'first reason')
                .withModuleMetadata()
                .publish()
            module('org.test', 'b', '1.0')
                .dependsOn(leaf2, reason: 'second reason')
                .withModuleMetadata()
                .publish()

        }
        FeaturePreviewsFixture.enableGradleMetadata(settingsFile)

        when:
        file("build.gradle") << """
            configurations {
                conf
            }
            
            repositories {
               maven { url "${mavenRepo.uri}" }
            }
            
            dependencies {
                conf 'org.test:a:1.0'
                conf 'org.test:b:1.0'
            }
            
            task checkDeps {
                doLast {
                    def result = configurations.conf.incoming.resolutionResult
                    result.allComponents {
                        if (it.id instanceof ModuleComponentIdentifier && it.id.module == 'leaf') {
                            def selectionReason = it.selectionReason
                            assert selectionReason.conflictResolution
                            def descriptions = selectionReason.descriptions.reverse()
                            assert descriptions.size() > 1
                            descriptions.each {
                                println "\$it.cause : \$it.description"
                            }
                            def descriptors = descriptions.findAll { it.cause == ComponentSelectionCause.REQUESTED }
                            assert descriptors.description == ['first reason', 'second reason']
                        }
                    }
                }
            }

        """

        then:
        run "checkDeps"
    }

    def "resolution result API gives access to dependency reasons in case of conflict and selection by rule"() {
        given:
        mavenRepo.with {
            def leaf1 = module('org.test', 'leaf', '1.0').publish()
            def leaf2 = module('org.test', 'leaf', '1.1').publish()
            module('org.test', 'a', '1.0')
                .dependsOn('org.test', 'leaf', '0.9')
                .withModuleMetadata()
                .publish()
            module('org.test', 'b', '1.0')
                .dependsOn(leaf2, reason: 'second reason')
                .withModuleMetadata()
                .publish()

        }
        settingsFile << """rootProject.name='test'"""
        FeaturePreviewsFixture.enableGradleMetadata(settingsFile)
        file("build.gradle") << """
            configurations {
                conf {
                    resolutionStrategy {
                        dependencySubstitution {
                            all {
                                if (it.requested instanceof ModuleComponentSelector) {
                                    if (it.requested.module == 'leaf' && it.requested.version == '0.9') {
                                        it.useTarget("substitute 0.9 with 1.0", group: 'org.test', name: it.requested.module, version: '1.0')
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
            repositories {
               maven { url "${mavenRepo.uri}" }
            }
            
            dependencies {
                conf 'org.test:a:1.0'
                conf 'org.test:b:1.0'
                
            }
        """
        resolve.prepare()
        buildFile << """           
            checkDeps {
                doLast {
                    def result = configurations.conf.incoming.resolutionResult
                    result.allComponents {
                        if (it.id instanceof ModuleComponentIdentifier && it.id.module == 'leaf') {
                            def selectionReason = it.selectionReason
                            assert selectionReason.conflictResolution
                            def descriptions = selectionReason.descriptions.reverse()
                            assert descriptions.size() > 1
                            descriptions.each {
                                println "\$it.cause : \$it.description"
                            }
                            def descriptors = descriptions.findAll { it.cause == ComponentSelectionCause.REQUESTED }
                            assert descriptors.description == ['requested', 'second reason']
                        }
                    }
                }
            }
        """

        when:

        run "checkDeps"

        then:
        resolve.expectGraph {
            root(":", ":test:") {
                module('org.test:a:1.0:runtime') {
                    edge('org.test:leaf:0.9', 'org.test:leaf:1.1')
                        .byConflictResolution("between versions 1.1 and 1.0") // conflict with the version requested by 'b'
                        .byReason('second reason') // this comes from 'b'
                        .selectedByRule("substitute 0.9 with 1.0")
                }
                module('org.test:b:1.0:runtime') {
                    module('org.test:leaf:1.1')
                        .selectedByRule("substitute 0.9 with 1.0")
                        .byConflictResolution("between versions 1.1 and 1.0")
                        .byReason('second reason')
                }
            }
        }
    }

    @Unroll
    def "constraint are not mis-showing up as a separate REQUESTED and do not overwrite selection by rule"() {
        given:
        mavenRepo.module("org", "foo", "1.0").publish()
        mavenRepo.module("org", "bar", "1.0").publish()

        buildFile << """

            repositories {
               maven { url "${mavenRepo.uri}" }
            }

            configurations {
                conf
            }
            dependencies {
                conf "org:foo:1.0"
                
                constraints {
                    conf("org:foo:1.0") {
                        version {
                            rejectAll()
                        }
                        if ($useReason) { because("This reason comes from a constraint") }
                    }
                }
            }
            
            configurations.all {
                resolutionStrategy.eachDependency {
                    if (requested.name == 'foo') {
                        because("fix comes from component selection rule").useTarget("org:bar:1.0")
                    }
                }
            }
            
            task checkWithApi {
                doLast {
                    def result = configurations.conf.incoming.resolutionResult
                    result.allComponents {
                        if (it.id instanceof ModuleComponentIdentifier) {
                            println "Module \$it.id"
                            it.selectionReason.descriptions.each {
                                println "   \$it.cause : \$it.description"
                            }
                        }
                    }
                }
            }
        """

        when:
        run 'checkWithApi'

        then:
        outputContains("""Module org:bar:1.0
   REQUESTED : requested
   SELECTED_BY_RULE : fix comes from component selection rule
   CONSTRAINT : ${useReason?'This reason comes from a constraint':'constraint'}
""")
        where:
        useReason << [true, false]
    }

    @Unroll
    def "direct dependency reasons are not mis-showing up as a separate REQUESTED and do not overwrite selection by rule"() {
        given:
        mavenRepo.module("org", "foo", "1.0").publish()
        mavenRepo.module("org", "bar", "1.0").publish()

        buildFile << """

            repositories {
               maven { url "${mavenRepo.uri}" }
            }

            configurations {
                conf
            }
            dependencies {
                conf("org:foo:1.0") {
                    if ($useReason) { because("This is a direct dependency reason") }
                }
            }
            
            configurations.all {
                resolutionStrategy.eachDependency {
                    if (requested.name == 'foo') {
                        because("fix comes from component selection rule").useTarget("org:bar:1.0")
                    }
                }
            }
            
            task checkWithApi {
                doLast {
                    def result = configurations.conf.incoming.resolutionResult
                    result.allComponents {
                        if (it.id instanceof ModuleComponentIdentifier) {
                            println "Module \$it.id"
                            it.selectionReason.descriptions.each {
                                println "   \$it.cause : \$it.description"
                            }
                        }
                    }
                }
            }
        """

        when:
        run 'checkWithApi'

        then:
        outputContains("""Module org:bar:1.0
   REQUESTED : ${useReason?'This is a direct dependency reason':'requested'}
   SELECTED_BY_RULE : fix comes from component selection rule
""")
        where:
        useReason << [true, false]
    }

    void "expired cache entry doesn't break reading from cache"() {
        given:
        mavenRepo.module("org", "foo", "1.0").publish()
        mavenRepo.module("org", "bar", "1.0").publish()

        buildFile << """

            repositories {
               maven { url "${mavenRepo.uri}" }
            }

            configurations {
                conf
            }
            
            def attr = Attribute.of("myAttribute", String)
            
            dependencies {
                conf("org:foo:1.0") {
                    because 'first reason' // must have custom reasons to show the problem
                }
                conf("org:bar") {
                    because 'second reason'
                    
                    attributes {
                        attribute(attr, 'val') // make sure attributes are properly serialized and read back
                    }
                }
                constraints {
                    conf("org:bar") {
                        version {
                            require "[0.1, 2.0["
                            prefer "1.0"
                        }
                    }
                }
            }
            
            task resolveTwice {
                doLast {
                    def result = configurations.conf.incoming.resolutionResult
                    result.allComponents { 
                        it.selectionReason.descriptions.each {
                           println "\${it.cause} : \${it.description}"
                        }
                    }
                    println 'Waiting for the cache to expire'
                    // see org.gradle.api.internal.artifacts.ivyservice.resolveengine.store.CachedStoreFactory
                    Thread.sleep(800) // must be > cache expiry
                    println 'Read result again'
                    result.allComponents {
                        it.selectionReason.descriptions.each {
                           println "\${it.cause} : \${it.description}"
                        }
                    }
                }
            }
        """
        executer.withArgument('-Dorg.gradle.api.internal.artifacts.ivyservice.resolveengine.store.cacheExpiryMs=500')

        when:
        run 'resolveTwice'

        then:
        noExceptionThrown()

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy