/*
 * 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

import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.integtests.fixtures.BuildOperationsFixture
import org.gradle.internal.featurelifecycle.DeprecatedUsageProgressDetails

class DeprecatedUsageBuildOperationProgressIntegrationTest extends AbstractIntegrationSpec {

    def operations = new BuildOperationsFixture(executer, temporaryFolder)

    def "emits deprecation warnings as build operation progress events with context"() {
        when:
        file('settings.gradle') << "rootProject.name = 'root'"

        file('init.gradle') << """
            org.gradle.util.DeprecationLogger.nagUserOfDeprecated('Init script');
        """

        file('script.gradle') << """
            org.gradle.util.DeprecationLogger.nagUserOfDeprecated('Plugin script');
        """

        buildScript """
            apply from: 'script.gradle'
            apply plugin: SomePlugin
            
            org.gradle.util.DeprecationLogger.nagUserWithDeprecatedBuildInvocationFeature('Some invocation feature', "Don't do custom invocation.")
            org.gradle.util.DeprecationLogger.nagUserWithDeprecatedIndirectUserCodeCause('Some indirect deprecation', 'Some advice.')
            
            task t(type:SomeTask) {
                doLast {
                    org.gradle.util.DeprecationLogger.nagUserWith('Custom Task action will be deprecated.', 
                        'Custom actions will not be supported in 2 weeks from now.',
                        'Use task type X instead.', 
                        "Task ':t' should not have custom actions attached."
                    );
                }
            }
            
            task t2(type:SomeTask)
            
            class SomePlugin implements Plugin<Project> {
                void apply(Project p){
                    org.gradle.util.DeprecationLogger.nagUserOfDeprecated('Plugin');
                }
            }
            
            class SomeTask extends DefaultTask {
                @TaskAction
                void someAction(){
                    org.gradle.util.DeprecationLogger.nagUserOfDeprecated('Typed task');
                }
            }
           
        """
        and:
        executer.noDeprecationChecks()
        succeeds 't','t2', '-I', 'init.gradle'

        then:
        def initDeprecation = operations.only("Apply script init.gradle to build").progress.find {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}
        initDeprecation.details.summary == 'Init script has been deprecated.'
        initDeprecation.details.removalDetails == 'This is scheduled to be removed in Gradle 6.0.'
        initDeprecation.details.advice == null
        initDeprecation.details.contextualAdvice == null
        initDeprecation.details.stackTrace.size > 0
        initDeprecation.details.stackTrace[0].fileName.endsWith('init.gradle')
        initDeprecation.details.stackTrace[0].lineNumber == 2

        def pluginDeprecation = operations.only("Apply plugin SomePlugin to root project 'root'").progress.find {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}
        pluginDeprecation.details.summary == 'Plugin has been deprecated.'
        pluginDeprecation.details.removalDetails == 'This is scheduled to be removed in Gradle 6.0.'
        pluginDeprecation.details.advice == null
        pluginDeprecation.details.contextualAdvice == null
        pluginDeprecation.details.type == 'USER_CODE_DIRECT'
        pluginDeprecation.details.stackTrace.size > 0
        pluginDeprecation.details.stackTrace[0].fileName.endsWith('build.gradle')
        pluginDeprecation.details.stackTrace[0].lineNumber == 22

        def invocationDeprecation = operations.only("Apply script build.gradle to root project 'root'").progress.findAll {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}[0]
        invocationDeprecation.details.summary == 'Some invocation feature has been deprecated.'
        invocationDeprecation.details.removalDetails == 'This is scheduled to be removed in Gradle 6.0.'
        invocationDeprecation.details.advice == "Don't do custom invocation."
        invocationDeprecation.details.type == "BUILD_INVOCATION"
        invocationDeprecation.details.stackTrace.size > 0
        invocationDeprecation.details.stackTrace[0].fileName.endsWith('build.gradle')
        invocationDeprecation.details.stackTrace[0].lineNumber == 5

        def userIndirectCodeDeprecation = operations.only("Apply script build.gradle to root project 'root'").progress.findAll {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}[1]
        userIndirectCodeDeprecation.details.summary == 'Some indirect deprecation has been deprecated.'
        userIndirectCodeDeprecation.details.removalDetails == 'This is scheduled to be removed in Gradle 6.0.'
        userIndirectCodeDeprecation.details.advice == "Some advice."
        userIndirectCodeDeprecation.details.type == 'USER_CODE_INDIRECT'
        userIndirectCodeDeprecation.details.stackTrace.size > 0
        userIndirectCodeDeprecation.details.stackTrace[0].fileName.endsWith('build.gradle')
        userIndirectCodeDeprecation.details.stackTrace[0].lineNumber == 6

        def scriptPluginDeprecation = operations.only("Apply script script.gradle to root project 'root'").progress.find {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}
        scriptPluginDeprecation.details.summary == 'Plugin script has been deprecated.'
        scriptPluginDeprecation.details.removalDetails == 'This is scheduled to be removed in Gradle 6.0.'
        scriptPluginDeprecation.details.advice == null
        scriptPluginDeprecation.details.type == 'USER_CODE_DIRECT'
        scriptPluginDeprecation.details.stackTrace.size > 0
        scriptPluginDeprecation.details.stackTrace[0].fileName.endsWith('script.gradle')
        scriptPluginDeprecation.details.stackTrace[0].lineNumber == 2

        def taskDoLastDeprecation = operations.only("Execute doLast {} action for :t").progress.find {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}
        taskDoLastDeprecation.details.summary == 'Custom Task action will be deprecated.'
        taskDoLastDeprecation.details.removalDetails == 'Custom actions will not be supported in 2 weeks from now.'
        taskDoLastDeprecation.details.advice == 'Use task type X instead.'
        taskDoLastDeprecation.details.contextualAdvice == "Task ':t' should not have custom actions attached."
        taskDoLastDeprecation.details.type == 'USER_CODE_DIRECT'
        taskDoLastDeprecation.details.stackTrace.size > 0
        taskDoLastDeprecation.details.stackTrace[0].fileName.endsWith('build.gradle')
        taskDoLastDeprecation.details.stackTrace[0].lineNumber == 10

        def typedTaskDeprecation = operations.only("Execute someAction for :t").progress.find {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}
        typedTaskDeprecation.details.summary == 'Typed task has been deprecated.'
        typedTaskDeprecation.details.removalDetails == 'This is scheduled to be removed in Gradle 6.0.'
        typedTaskDeprecation.details.advice == null
        typedTaskDeprecation.details.contextualAdvice == null
        typedTaskDeprecation.details.type == 'USER_CODE_DIRECT'
        typedTaskDeprecation.details.stackTrace.size > 0
        typedTaskDeprecation.details.stackTrace[0].fileName.endsWith('build.gradle')
        typedTaskDeprecation.details.stackTrace[0].lineNumber == 29
        typedTaskDeprecation.details.stackTrace[0].methodName == 'someAction'

        def typedTaskDeprecation2 = operations.only("Execute someAction for :t2").progress.find {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}
        typedTaskDeprecation2.details.summary == 'Typed task has been deprecated.'
        typedTaskDeprecation2.details.removalDetails == 'This is scheduled to be removed in Gradle 6.0.'
        typedTaskDeprecation2.details.advice == null
        typedTaskDeprecation2.details.contextualAdvice == null
        typedTaskDeprecation2.details.type == 'USER_CODE_DIRECT'
        typedTaskDeprecation2.details.stackTrace.size > 0
        typedTaskDeprecation2.details.stackTrace[0].fileName.endsWith('build.gradle')
        typedTaskDeprecation2.details.stackTrace[0].lineNumber == 29
        typedTaskDeprecation2.details.stackTrace[0].methodName == 'someAction'
    }

    def "emits deprecation warnings as build operation progress events for buildSrc builds"() {
        when:
        file('buildSrc/build.gradle') << """
            org.gradle.util.DeprecationLogger.nagUserOfDeprecated('BuildSrc script');
        """

        and:
        executer.noDeprecationChecks()
        succeeds 'help'

        then:
        def buildSrcDeprecations = operations.only("Apply script build.gradle to project ':buildSrc'").progress.find {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}
        buildSrcDeprecations.details.summary.contains('BuildSrc script has been deprecated.')
        buildSrcDeprecations.details.removalDetails.contains('This is scheduled to be removed in Gradle 6.0.')
        buildSrcDeprecations.details.advice == null
        buildSrcDeprecations.details.contextualAdvice == null
        buildSrcDeprecations.details.type == 'USER_CODE_DIRECT'
        buildSrcDeprecations.details.stackTrace.size > 0
        buildSrcDeprecations.details.stackTrace[0].fileName.endsWith("buildSrc${File.separator}build.gradle")
        buildSrcDeprecations.details.stackTrace[0].lineNumber == 2
    }

    def "emits deprecation warnings as build operation progress events for composite builds"() {
        file('included/settings.gradle') << "rootProject.name = 'included'"
        file('included/build.gradle') << """
            org.gradle.util.DeprecationLogger.nagUserOfDeprecated('Included build script');
            
            task t {
                doLast {
                    org.gradle.util.DeprecationLogger.nagUserOfDeprecated('Included build task');
                }
            }
        """
        file('settings.gradle') << """
        rootProject.name = 'root'
        includeBuild('included')
        """

        when:
        buildFile << """
            task t { dependsOn gradle.includedBuilds*.task(':t') } 
        """

        and:
        executer.noDeprecationChecks()
        succeeds 't'

        then:
        def includedBuildScriptDeprecations = operations.only("Apply script build.gradle to project ':included'").progress.find {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}
        includedBuildScriptDeprecations.details.summary == 'Included build script has been deprecated.'
        includedBuildScriptDeprecations.details.removalDetails == 'This is scheduled to be removed in Gradle 6.0.'
        includedBuildScriptDeprecations.details.advice == null
        includedBuildScriptDeprecations.details.contextualAdvice == null
        includedBuildScriptDeprecations.details.type == 'USER_CODE_DIRECT'
        includedBuildScriptDeprecations.details.stackTrace.size > 0
        includedBuildScriptDeprecations.details.stackTrace[0].fileName.endsWith("included${File.separator}build.gradle")
        includedBuildScriptDeprecations.details.stackTrace[0].lineNumber == 2

        def includedBuildTaskDeprecations = operations.only("Execute doLast {} action for :included:t").progress.find {it.hasDetailsOfType(DeprecatedUsageProgressDetails)}
        includedBuildTaskDeprecations.details.summary == 'Included build task has been deprecated.'
        includedBuildTaskDeprecations.details.removalDetails == 'This is scheduled to be removed in Gradle 6.0.'
        includedBuildTaskDeprecations.details.advice == null
        includedBuildTaskDeprecations.details.contextualAdvice == null
        includedBuildTaskDeprecations.details.type == 'USER_CODE_DIRECT'
        includedBuildTaskDeprecations.details.stackTrace.size > 0
        includedBuildTaskDeprecations.details.stackTrace[0].fileName.endsWith("included${File.separator}build.gradle")
        includedBuildTaskDeprecations.details.stackTrace[0].lineNumber == 6
    }
}
