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

import groovy.transform.NotYetImplemented
import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.integtests.fixtures.StaleOutputJavaProject
import org.gradle.integtests.fixtures.UnsupportedWithInstantExecution
import org.gradle.integtests.fixtures.executer.ExecutionResult
import org.gradle.integtests.fixtures.executer.GradleExecuter
import org.gradle.integtests.fixtures.timeout.IntegrationTestTimeout
import org.gradle.integtests.fixtures.versions.ReleasedVersionDistributions
import org.gradle.internal.jvm.Jvm
import org.junit.Assume
import spock.lang.Issue
import spock.lang.Unroll

import static org.gradle.integtests.fixtures.StaleOutputJavaProject.JAR_TASK_NAME
import static org.gradle.util.GFileUtils.forceDelete

@IntegrationTestTimeout(240)
class StaleOutputHistoryLossIntegrationTest extends AbstractIntegrationSpec {

    private final ReleasedVersionDistributions releasedVersionDistributions = new ReleasedVersionDistributions()
    private final GradleExecuter mostRecentReleaseExecuter = releasedVersionDistributions.mostRecentRelease.executer(temporaryFolder, buildContext)

    def cleanup() {
        mostRecentReleaseExecuter.cleanup()
    }

    def setup() {
        buildFile << "apply plugin: 'base'\n"
        // When adding support for a new JDK version, the previous release might not work with it yet.
        Assume.assumeTrue(releasedVersionDistributions.mostRecentRelease.worksWith(Jvm.current()))
    }

    @Issue("https://github.com/gradle/gradle/issues/821")
    @Unroll
    def "production class files are removed in a single project build for #description"() {
        given:
        def javaProject = new StaleOutputJavaProject(testDirectory, buildDirName)
        buildFile << "apply plugin: 'java'"

        if (!defaultDir) {
            buildFile << """
                buildDir = '$javaProject.buildDirName'
            """
        }

        when:
        result = runWithMostRecentFinalRelease(JAR_TASK_NAME)

        then:
        javaProject.mainClassFile.assertIsFile()
        javaProject.redundantClassFile.assertIsFile()
        javaProject.assertJarHasDescendants(javaProject.mainClassFile.name, javaProject.redundantClassFile.name)

        when:
        forceDelete(javaProject.redundantSourceFile)
        succeeds JAR_TASK_NAME

        then:
        javaProject.mainClassFile.assertIsFile()
        javaProject.redundantClassFile.assertDoesNotExist()
        javaProject.assertJarHasDescendants(javaProject.mainClassFile.name)

        when:
        succeeds JAR_TASK_NAME

        then:
        javaProject.assertBuildTasksSkipped(result)

        where:
        buildDirName | defaultDir | description
        'build'      | true       | 'default build directory'
        'out'        | false      | 'reconfigured build directory'
    }

    def "production class files outside of 'build' are removed"() {
        given:
        def javaProject = new StaleOutputJavaProject(testDirectory, 'out')
        buildFile << """
            apply plugin: 'java'

            sourceSets {
                main {
                    java.outputDir = file('out/classes/java/main')
                }
            }
        """.stripIndent()

        when:
        result = runWithMostRecentFinalRelease(JAR_TASK_NAME)

        then:
        javaProject.mainClassFile.assertIsFile()
        javaProject.redundantClassFile.assertIsFile()

        when:
        forceDelete(javaProject.redundantSourceFile)
        succeeds JAR_TASK_NAME

        then:
        javaProject.mainClassFile.assertIsFile()
        javaProject.redundantClassFile.assertDoesNotExist()

        when:
        succeeds JAR_TASK_NAME

        then:
        javaProject.assertBuildTasksSkipped(result)
    }

    // We register the output directory before task execution and would have deleted output files at the end of configuration.
    @Issue("https://github.com/gradle/gradle/issues/821")
    @UnsupportedWithInstantExecution
    def "production class files are removed even if output directory is reconfigured during execution phase"() {
        given:
        def javaProject = new StaleOutputJavaProject(testDirectory)
        buildFile << """
            apply plugin: 'java'

            task configureCompileJava {
                doLast {
                    compileJava.destinationDir = file('build/out')
                    jar.from compileJava
                }
            }

            compileJava.dependsOn configureCompileJava
        """
        when:
        result = runWithMostRecentFinalRelease(JAR_TASK_NAME)

        then:
        javaProject.assertBuildTasksExecuted(result)
        javaProject.mainClassFile.assertDoesNotExist()
        javaProject.redundantClassFile.assertDoesNotExist()
        javaProject.assertJarHasDescendants(javaProject.mainClassFile.name, javaProject.redundantClassFile.name)
        javaProject.mainClassFileAlternate.assertIsFile()
        javaProject.redundantClassFileAlternate.assertIsFile()

        when:
        buildFile.text = buildFile.text.replace('compileJava.destinationDir', 'compileJava.destinationDirectory')
        forceDelete(javaProject.redundantSourceFile)
        succeeds JAR_TASK_NAME

        then:
        javaProject.assertBuildTasksExecuted(result)
        javaProject.assertJarHasDescendants(javaProject.mainClassFile.name)
        javaProject.mainClassFileAlternate.assertIsFile()
        javaProject.redundantClassFileAlternate.assertDoesNotExist()

        when:
        succeeds JAR_TASK_NAME

        then:
        javaProject.assertBuildTasksSkipped(result)
    }

    @Unroll
    @Issue("https://github.com/gradle/gradle/issues/821")
    def "production class files are removed in a multi-project build executed #description"(String[] arguments, String description) {
        given:
        def projectCount = 3
        def javaProjects = (1..projectCount).collect {
            def projectName = createProjectName(it)
            file("${projectName}/build.gradle") << "apply plugin: 'java'"
            new StaleOutputJavaProject(testDirectory, "build", projectName)
        }
<commit_msg>Fix test which had duplicate classes in 6.7<commit_after>/*
 * 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

import groovy.transform.NotYetImplemented
import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.integtests.fixtures.StaleOutputJavaProject
import org.gradle.integtests.fixtures.UnsupportedWithInstantExecution
import org.gradle.integtests.fixtures.executer.ExecutionResult
import org.gradle.integtests.fixtures.executer.GradleExecuter
import org.gradle.integtests.fixtures.timeout.IntegrationTestTimeout
import org.gradle.integtests.fixtures.versions.ReleasedVersionDistributions
import org.gradle.internal.jvm.Jvm
import org.gradle.util.GradleVersion
import org.junit.Assume
import spock.lang.Issue
import spock.lang.Unroll

import static org.gradle.integtests.fixtures.StaleOutputJavaProject.JAR_TASK_NAME
import static org.gradle.util.GFileUtils.forceDelete

@IntegrationTestTimeout(240)
class StaleOutputHistoryLossIntegrationTest extends AbstractIntegrationSpec {

    private final ReleasedVersionDistributions releasedVersionDistributions = new ReleasedVersionDistributions()
    private final GradleExecuter mostRecentReleaseExecuter = releasedVersionDistributions.mostRecentRelease.executer(temporaryFolder, buildContext)

    def cleanup() {
        mostRecentReleaseExecuter.cleanup()
    }

    def setup() {
        buildFile << "apply plugin: 'base'\n"
        // When adding support for a new JDK version, the previous release might not work with it yet.
        Assume.assumeTrue(releasedVersionDistributions.mostRecentRelease.worksWith(Jvm.current()))
    }

    GradleVersion getMostRecentReleaseVersion() {
        releasedVersionDistributions.mostRecentRelease.version
    }

    @Issue("https://github.com/gradle/gradle/issues/821")
    @Unroll
    def "production class files are removed in a single project build for #description"() {
        given:
        def javaProject = new StaleOutputJavaProject(testDirectory, buildDirName)
        buildFile << "apply plugin: 'java'"

        if (!defaultDir) {
            buildFile << """
                buildDir = '$javaProject.buildDirName'
            """
        }

        when:
        result = runWithMostRecentFinalRelease(JAR_TASK_NAME)

        then:
        javaProject.mainClassFile.assertIsFile()
        javaProject.redundantClassFile.assertIsFile()
        javaProject.assertJarHasDescendants(javaProject.mainClassFile.name, javaProject.redundantClassFile.name)

        when:
        forceDelete(javaProject.redundantSourceFile)
        succeeds JAR_TASK_NAME

        then:
        javaProject.mainClassFile.assertIsFile()
        javaProject.redundantClassFile.assertDoesNotExist()
        javaProject.assertJarHasDescendants(javaProject.mainClassFile.name)

        when:
        succeeds JAR_TASK_NAME

        then:
        javaProject.assertBuildTasksSkipped(result)

        where:
        buildDirName | defaultDir | description
        'build'      | true       | 'default build directory'
        'out'        | false      | 'reconfigured build directory'
    }

    def "production class files outside of 'build' are removed"() {
        given:
        def javaProject = new StaleOutputJavaProject(testDirectory, 'out')
        buildFile << """
            apply plugin: 'java'

            sourceSets {
                main {
                    java.outputDir = file('out/classes/java/main')
                }
            }
        """.stripIndent()

        when:
        result = runWithMostRecentFinalRelease(JAR_TASK_NAME)

        then:
        javaProject.mainClassFile.assertIsFile()
        javaProject.redundantClassFile.assertIsFile()

        when:
        forceDelete(javaProject.redundantSourceFile)
        succeeds JAR_TASK_NAME

        then:
        javaProject.mainClassFile.assertIsFile()
        javaProject.redundantClassFile.assertDoesNotExist()

        when:
        succeeds JAR_TASK_NAME

        then:
        javaProject.assertBuildTasksSkipped(result)
    }

    // We register the output directory before task execution and would have deleted output files at the end of configuration.
    @Issue("https://github.com/gradle/gradle/issues/821")
    @UnsupportedWithInstantExecution
    def "production class files are removed even if output directory is reconfigured during execution phase"() {
        given:
        def javaProject = new StaleOutputJavaProject(testDirectory)
        // This can be removed once 6.7 is out!
        def workaroundForNonLazyCompileJava = mostRecentReleaseVersion <= GradleVersion.version("6.6") ? 'jar.from compileJava' : ''
        buildFile << """
            apply plugin: 'java'

            task configureCompileJava {
                doLast {
                    compileJava.destinationDir = file('build/out')
                    // In Gradle <6.7, `compileJava` wasn't "lazy enough" that the jar included
                    // the change in the output directory. It is now so if we want to avoid
                    // duplicates in the jar we need to include only in <6.7
                    ${workaroundForNonLazyCompileJava}
                }
            }

            compileJava.dependsOn configureCompileJava
        """
        when:
        result = runWithMostRecentFinalRelease(JAR_TASK_NAME)

        then:
        javaProject.assertBuildTasksExecuted(result)
        javaProject.mainClassFile.assertDoesNotExist()
        javaProject.redundantClassFile.assertDoesNotExist()
        javaProject.assertJarHasDescendants(javaProject.mainClassFile.name, javaProject.redundantClassFile.name)
        javaProject.mainClassFileAlternate.assertIsFile()
        javaProject.redundantClassFileAlternate.assertIsFile()

        when:
        buildFile.text = buildFile.text.replace(workaroundForNonLazyCompileJava, '')
        forceDelete(javaProject.redundantSourceFile)
        succeeds JAR_TASK_NAME

        then:
        javaProject.assertBuildTasksExecuted(result)
        javaProject.assertJarHasDescendants(javaProject.mainClassFile.name)
        javaProject.mainClassFileAlternate.assertIsFile()
        javaProject.redundantClassFileAlternate.assertDoesNotExist()

        when:
        succeeds JAR_TASK_NAME

        then:
        javaProject.assertBuildTasksSkipped(result)
    }

    @Unroll
    @Issue("https://github.com/gradle/gradle/issues/821")
    def "production class files are removed in a multi-project build executed #description"(String[] arguments, String description) {
        given:
        def projectCount = 3
        def javaProjects = (1..projectCount).collect {
            def projectName = createProjectName(it)
            file("${projectName}/build.gradle") << "apply plugin: 'java'"
            new StaleOutputJavaProject(testDirectory, "build", projectName)
        }

{'fraction_non_alphanumeric': 0.0750105648682913, 'fraction_numerical': 0.0038033525848711084, 'mean_word_length': 2.945262572936927, 'pattern_counts': {'":': 0, '<': 30, '<?xml version=': 0, '>': 3, 'https://': 6, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '989206', 'n_tokens_mistral': 3889, 'n_tokens_neox': 3705, 'n_words': 833}