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

import groovy.test.NotYetImplemented
import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.integtests.fixtures.resolve.ResolveTestFixture

class DirectoryOutputArtifactIntegrationTest extends AbstractIntegrationSpec {
    ResolveTestFixture resolve = new ResolveTestFixture(buildFile)

    def "can attach a directory as output of a configuration"() {
        given:
        file('someDir/a.txt') << 'some text'
        buildFile << '''

        configurations {
            compile
            _classpath
        }

        artifacts {
            _classpath file("someDir")
        }

        dependencies {
            compile project(path: ':', configuration: '_classpath')
        }

        def files = configurations.compile

        task check {
            doLast {
                println files as List
                assert files*.name == ['someDir']
            }
        }

        task run(dependsOn: configurations.compile) {
            doLast {
                assert files*.listFiles().flatten().text == ['some text']
            }
        }
        '''

        when:
        run 'check'

        then:
        executed ':check'

        when:
        run 'run'

        then:
        executed ':run'
    }

    /**
     * This is not a use case we want to support at the moment. There's no need to immediately fix this.
     */
    @NotYetImplemented
    def "can attach a directory as output of a configuration generated by another task"() {
        given:
        buildFile << '''

       configurations {
            compile
            _classpath
        }

        dependencies {
            compile project(path: ':', configuration: '_classpath')
        }

       task generateFiles {
            ext.outputDir = file("$buildDir/someDir")
            doLast {
                ext.outputDir.mkdirs()
                file("${ext.outputDir}/a.txt") << 'some text'
            }
        }

        artifacts {
            _classpath file:file("someDir"), builtBy: generateFiles
        }

        task check {
            doLast {
                println configurations.compile.files as List
                assert configurations.compile.files.name == ['someDir']
            }
        }

        task run(dependsOn: configurations.compile) {
            doLast {
                assert configurations.compile.files*.listFiles().flatten().text == ['some text']
            }
        }

        '''

        when:
        run 'check'

        then:
        notExecuted ':generateFiles'

        when:
        run 'run'

        then:
        executedAndNotSkipped ':generateFiles'
    }

    def "can attach a directory as output of a configuration generated by another task in a different project"() {
        given:
        file('settings.gradle') << "include 'a', 'b'"
        file('a/build.gradle') << '''

        configurations {
            compile
        }

        dependencies {
            compile project(path: ':b', configuration: 'compile')
        }

        def files = configurations.compile

        task check {
            doLast {
                println files as List
                assert files*.name == ['someDir']
            }
        }

        task run(dependsOn: configurations.compile) {
            doLast {
                assert files*.listFiles().flatten().text == ['some text']
            }
        }
        '''

        file('b/build.gradle') << '''

        configurations {
            compile
        }

        task generateFiles {
            def outputDir = file("$buildDir/someDir")
            outputs.dir(outputDir)
            doLast {
                new File(outputDir, "a.txt") << 'some text'
            }
        }

        artifacts {
            compile file:file("$buildDir/someDir"), builtBy: generateFiles
        }
        '''

        when:
        run 'a:check'

        then:
        notExecuted ':b:generateFiles'

        when:
        run 'a:run'

        then:
        executedAndNotSkipped ':b:generateFiles'
    }

    def "can avoid building a jar when compiling against another project"() {
        given:
        file('settings.gradle') << "include 'a', 'b'"
        file('a/build.gradle') << '''

        apply plugin: 'java'

        dependencies {
            implementation project(path: ':b', configuration: 'compile_output')
        }

        '''
        file('a/src/main/java/World.java') << 'public class World extends Hello {}'
        file('b/build.gradle') << '''

        apply plugin: 'java'

        configurations {
            compile_output
        }

        artifacts {
            compile_output file:compileJava.destinationDirectory.asFile.get(), builtBy: compileJava
        }
        '''
        file('b/src/main/java/Hello.java') << 'public class Hello {}'

        when:
        run 'a:compileJava'

        then:
        executedAndNotSkipped ':b:compileJava'
        notExecuted ':b:jar'

    }

    def "can avoid building a jar when compiling against another project with transitive dependencies"() {
        given:
        file('settings.gradle') << "include 'a', 'b'"
        file('a/build.gradle') << """

        ${mavenCentralRepository()}

        apply plugin: 'java'

        dependencies {
            implementation project(path: ':b', configuration: 'compile_output')
        }

        """
        file('a/src/main/java/World.java') << '''import org.apache.commons.lang3.StringUtils;

        public class World extends Hello {
            public String scream(String name) {
                return "HELLO, " + StringUtils.capitalize(name);
            }
        }

        '''

        file('b/build.gradle') << """

        apply plugin: 'java'

        ${mavenCentralRepository()}

        configurations {
            compile_output {
               extendsFrom implementation
            }
        }

        dependencies {
            implementation 'org.apache.commons:commons-lang3:3.5'
        }

        artifacts {
            compile_output file:compileJava.destinationDirectory.asFile.get(), builtBy: compileJava
        }
        """
        file('b/src/main/java/Hello.java') << '''import org.apache.commons.lang3.StringUtils;
            public class Hello {
                String greet(String name) { return "Hello, " + StringUtils.capitalize(name); }
            }
        '''

        when:
        run 'a:compileJava'

        then:
        executedAndNotSkipped ':b:compileJava'
        notExecuted ':b:jar'

    }

    def "doesn't throw an NPE when loading results from disk"() {
        file('someDir/a.txt') << 'some text'
        file('settings.gradle') << 'rootProject.name="nullsafe"'
        buildFile << '''
        version = '1.0'

        configurations {
            compile
            _classpath
        }

        artifacts {
            _classpath file("someDir")
        }

        dependencies {
            compile project(path: ':', configuration: '_classpath')
        }

        task run {
            doLast {
                assert configurations.compile.resolvedConfiguration.firstLevelModuleDependencies.name == [':nullsafe:1.0']
            }
        }
        '''
        resolve.prepare("compile")

        when:
        run 'checkDeps'

        then:
        resolve.expectGraph {
            root(":", ":nullsafe:1.0") {
                configuration("_classpath")
                artifact(name: "someDir", type: "", version: "")
                project(":", ":nullsafe:1.0")
            }
        }
    }
}
