<commit_before>/*
 * Copyright 2015 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.language.java
import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.test.fixtures.file.LeaksFileHandles
import spock.lang.Ignore

@LeaksFileHandles
class JavaCompilationAgainstApiJarIntegrationTest extends AbstractIntegrationSpec {
    void applyJavaPlugin(File buildFile) {
        buildFile << '''
plugins {
    id 'jvm-component'
    id 'java-lang'
}
'''
    }

    private void mainLibraryDependingOnApi() {
        buildFile << '''
model {
    components {
        myLib(JvmLibrarySpec) {
            api {
                exports 'com.acme'
            }
        }
        main(JvmLibrarySpec) {
            sources {
                java {
                    dependencies {
                        library 'myLib'
                    }
                }
            }
        }
    }
}
'''
    }

    private void testAppDependingOnApiClass() {
        file('src/main/java/com/acme/TestApp.java') << '''package com.acme;

import com.acme.Person;

public class TestApp {
    private Person person;
}

'''
    }

    void updateFile(String path, String contents) {
        // add a small delay in order to avoid FS synchronization issues
        // the errors often look like this:
        // bad class file: /home/cchampeau/DEV/PROJECTS/GITHUB/gradle/subprojects/language-java/build/tmp/test files/JavaCompilationAgainstApiJarIntegrationTest/consuming_source_is...s_changes/qgrf/build/jars/myLibApiJar/myLib.jar(com/acme/Person.class)
        // unable to access file: corrupted zip file
        // and the reason is unclear now.
        // TODO: investigate this issue
        sleep(1000)
        file(path).write(contents)
    }

    def "fails compilation if trying to compile a non-API class"() {
        given:
        applyJavaPlugin(buildFile)
        mainLibraryDependingOnApi()

        file('src/myLib/java/com/acme/Person.java') << '''package com.acme;

public class Person {}
'''
        file('src/myLib/java/internal/PersonInternal.java') << '''package internal;
import com.acme.Person;

public class PersonInternal extends Person {}
'''

        and:
        file('src/main/java/com/acme/TestApp.java') << '''package com.acme;

import internal.PersonInternal;

public class TestApp {
    private PersonInternal person;
}

'''

        expect:
        succeeds ':myLibJar'

        and:
        fails ':mainJar'
    }

    def "consuming source is recompiled when API class changes"() {
        given:
        applyJavaPlugin(buildFile)
        mainLibraryDependingOnApi()

        file('src/myLib/java/com/acme/Person.java') << '''package com.acme;

public class Person {}
'''
        file('src/myLib/java/internal/PersonInternal.java') << '''package internal;
import com.acme.Person;

public class PersonInternal extends Person {}
'''

        and:
        testAppDependingOnApiClass()

        expect:
        succeeds ':myLibJar'

        and:
        succeeds ':mainJar'

        when:
        updateFile('src/myLib/java/com/acme/Person.java', '''package com.acme;

public class Person {
    public String name;
}
''')
        then:
        succeeds ':mainJar'

        and:
        executedAndNotSkipped(':compileMyLibJarMyLibJava')
        executedAndNotSkipped(':compileMainJarMainJava')
    }

    def "consuming source is not recompiled when non-API class changes"() {
        given:
        applyJavaPlugin(buildFile)
        mainLibraryDependingOnApi()

        file('src/myLib/java/com/acme/Person.java') << '''package com.acme;

public class Person {}
'''
        file('src/myLib/java/internal/PersonInternal.java') << '''package internal;
import com.acme.Person;

public class PersonInternal extends Person {}
'''

        and:
        testAppDependingOnApiClass()

        expect:
        succeeds ':myLibJar'

        and:
        succeeds ':mainJar'

        when:
        updateFile('src/myLib/java/internal/PersonInternal.java', '''package internal;
import com.acme.Person;

public class PersonInternal extends Person {
    private String name;
}
''')
        then:
        succeeds ':mainJar'

        and:
        executedAndNotSkipped(':compileMyLibJarMyLibJava')
        skipped(':compileMainJarMainJava')
    }

    def "consuming source is not recompiled when comment is changed in API class"() {
        given:
        applyJavaPlugin(buildFile)
        mainLibraryDependingOnApi()

        file('src/myLib/java/com/acme/Person.java') << '''package com.acme;

public class Person {
    private String name;
    public String toString() { return name; }
}
'''

        and:
        testAppDependingOnApiClass()

        expect:<commit_msg>Add compilation test cases for API dependencies<commit_after>/*
 * Copyright 2015 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.language.java
import org.gradle.integtests.fixtures.AbstractIntegrationSpec
import org.gradle.test.fixtures.file.LeaksFileHandles
import spock.lang.Ignore
import spock.lang.Unroll

@LeaksFileHandles
class JavaCompilationAgainstApiJarIntegrationTest extends AbstractIntegrationSpec {
    void applyJavaPlugin(File buildFile) {
        buildFile << '''
plugins {
    id 'jvm-component'
    id 'java-lang'
}
'''
    }

    private void mainLibraryDependingOnApi() {
        mainLibraryDependingOnApi(DependencyScope.SOURCES)
    }

    private void mainLibraryDependingOnApi(DependencyScope scope) {
        buildFile << """
model {
    components {
        myLib(JvmLibrarySpec) {
            api {
                exports 'com.acme'
            }
        }
        main(JvmLibrarySpec) {
            ${scope.declaration}
        }
    }
}
"""
    }

    static enum DependencyScope {
        API {
            @Override
            public String getDeclaration() {
                return '''
                api {
                    dependencies {
                        library 'myLib'
                    }
                }'''
            }
        },
        SOURCES {
            @Override
            public String getDeclaration() {
                return '''
                sources {
                    java {
                        dependencies {
                            library 'myLib'
                        }
                    }
                }'''
            }
        };

        public abstract String getDeclaration();
    }

    static Collection<DependencyScope> scopes = [
        //TODO:RBO uncomment the following line
        //DependencyScope.API,
        DependencyScope.SOURCES
    ]

    private void testAppDependingOnApiClass() {
        file('src/main/java/com/acme/TestApp.java') << '''package com.acme;

import com.acme.Person;

public class TestApp {
    private Person person;
}

'''
    }

    void updateFile(String path, String contents) {
        // add a small delay in order to avoid FS synchronization issues
        // the errors often look like this:
        // bad class file: /home/cchampeau/DEV/PROJECTS/GITHUB/gradle/subprojects/language-java/build/tmp/test files/JavaCompilationAgainstApiJarIntegrationTest/consuming_source_is...s_changes/qgrf/build/jars/myLibApiJar/myLib.jar(com/acme/Person.class)
        // unable to access file: corrupted zip file
        // and the reason is unclear now.
        // TODO: investigate this issue
        sleep(1000)
        file(path).write(contents)
    }

    @Unroll
    def "fails compilation if trying to compile a non-API class (#scope)"() {
        given:
        applyJavaPlugin(buildFile)
        mainLibraryDependingOnApi(scope)

        file('src/myLib/java/com/acme/Person.java') << '''package com.acme;

public class Person {}
'''
        file('src/myLib/java/internal/PersonInternal.java') << '''package internal;
import com.acme.Person;

public class PersonInternal extends Person {}
'''

        and:
        file('src/main/java/com/acme/TestApp.java') << '''package com.acme;

import internal.PersonInternal;

public class TestApp {
    private PersonInternal person;
}

'''

        expect:
        succeeds ':myLibJar'

        and:
        fails ':mainJar'

        where:
        scope << scopes
    }

    @Unroll
    def "consuming source is recompiled when API class changes (#scope)"() {
        given:
        applyJavaPlugin(buildFile)
        mainLibraryDependingOnApi(scope)

        file('src/myLib/java/com/acme/Person.java') << '''package com.acme;

public class Person {}
'''
        file('src/myLib/java/internal/PersonInternal.java') << '''package internal;
import com.acme.Person;

public class PersonInternal extends Person {}
'''

        and:
        testAppDependingOnApiClass()

        expect:
        succeeds ':myLibJar'

        and:
        succeeds ':mainJar'

        when:
        updateFile('src/myLib/java/com/acme/Person.java', '''package com.acme;

public class Person {
    public String name;
}
''')
        then:
        succeeds ':mainJar'

        and:
        executedAndNotSkipped(':compileMyLibJarMyLibJava')
        executedAndNotSkipped(':compileMainJarMainJava')

        where:
        scope << scopes
    }

    @Unroll
    def "consuming source is not recompiled when non-API class changes (#scope)"() {
        given:
        applyJavaPlugin(buildFile)
        mainLibraryDependingOnApi(scope)

        file('src/myLib/java/com/acme/Person.java') << '''package com.acme;

public class Person {}
'''
        file('src/myLib/java/internal/PersonInternal.java') << '''package internal;
import com.acme.Person;

public class PersonInternal extends Person {}
'''

        and:
        testAppDependingOnApiClass()

        expect:
        succeeds ':myLibJar'

        and:
        succeeds ':mainJar'

        when:
        updateFile('src/myLib/java/internal/PersonInternal.java', '''package internal;
import com.acme.Person;

public class PersonInternal extends Person {
    private String name;
}
''')
        then:
        succeeds ':mainJar'

        and:
        executedAndNotSkipped(':compileMyLibJarMyLibJava')
        skipped(':compileMainJarMainJava')

        where:
        scope << scopes
    }

    def "consuming source is not recompiled when comment is changed in API class"() {
        given:
        applyJavaPlugin(buildFile)
        mainLibraryDependingOnApi()

        file('src/myLib/java/com/acme/Person.java') << '''package com.acme;

public class Person {
    private String name;
    public String toString() { return name; }
}
'''

        and:
        testAppDependingOnApiClass()

        expect:
{'fraction_non_alphanumeric': 0.10182005494505494, 'fraction_numerical': 0.0020604395604395605, 'mean_word_length': 2.3320938215102975, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 4, 'https://': 0, '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': '5147989', 'n_tokens_mistral': 3381, 'n_tokens_neox': 3257, 'n_words': 807}