<commit_before>package org.gosulang.gradle.functional.classpath

import org.gosulang.gradle.functional.AbstractGosuPluginSpecification
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.UnexpectedBuildFailure
import org.junit.rules.TemporaryFolder

import java.util.regex.Matcher

import static org.fest.assertions.Assertions.*
import static org.gradle.testkit.runner.TaskOutcome.*

/**
 * commons, base and app are three projects. On the file system, they are peers sitting at the same level on the tree
 */
class MultiModuleGraphTraversalTest extends AbstractGosuPluginSpecification {
    TemporaryFolder rootFolder
    File baseTestFolder
    File appFolder
    File appExtFolder
    File appTestFolder

    File rootProjectSettings

    GString rootBuildScriptText
    
    File rootBuildScript
    File appExtBuildScript
    File appTestBuildScript

    /**
     * super#setup is invoked automatically
     * @return
     */
    @Override
    def setup() {
        rootFolder = testProjectDir
        baseTestFolder = rootFolder.newFolder('baseTest')
        appFolder = rootFolder.newFolder('app')
        appExtFolder = rootFolder.newFolder('appExt')
        appTestFolder = rootFolder.newFolder('appTest')
        
        rootProjectSettings = rootFolder.newFile('settings.gradle')
        
        rootBuildScript = buildScript //from superclass
        appExtBuildScript = new File(appExtFolder, 'build.gradle')
        appTestBuildScript = new File(appTestFolder, 'build.gradle')
        
        rootBuildScript <<
            """
            plugins {
                id 'org.gosu-lang.gosu'
            }

            allprojects {
                apply plugin: 'org.gosu-lang.gosu'
                repositories {
                    mavenLocal()
                    mavenCentral()
                    maven {
                        url 'https://oss.sonatype.org/content/repositories/snapshots' //for Gosu snapshot builds
                    }
                }
                dependencies {
                    compile group: 'org.gosu-lang.gosu', name: 'gosu-core-api', version: '$gosuVersion'
                }
                task printClasspath << {
                    println 'The classpath is: ' + compileGosu.classpath.files.collect { it.name } //configurations.compile.files.collect { it.name }
                }
            }
            """
        
        rootProjectSettings <<
            """
            include ':baseTest'
            include ':app'
            include ':appExt'
            include ':appTest'
            """

        appExtBuildScript << 
            """
            dependencies {
                compile project(':app')
            }
            """

        appTestBuildScript <<
            """
            dependencies {
                compile project(':appExt')
                compile project(':baseTest')
            }
            """
    }

    def 'test breadth-first traversal (Gradle default behavior)'() {
        when:
        GradleRunner runner = GradleRunner.create()
                .withProjectDir(rootFolder.root)
                .withPluginClasspath(pluginClasspath)
                .withArguments(':appTest:printClasspath', ':appTest:test', '-is')

        BuildResult result = runner.build()

        then:
        notThrown(UnexpectedBuildFailure)
        println(result.standardOutput)
        result.standardError.empty

        String[] expected = ['appExt.jar', 'baseTest.jar', 'app.jar']

        String[] orderedClasspath = getOrderedClasspath(result.standardOutput)

        println('orderedClasspath is: ' + orderedClasspath)

        //filter gosu dependencies not related to this test
        List<String> actual = orderedClasspath.findAll { expected.contains(it) }

        assertThat(actual).containsExactly(expected)

        result.task(':appTest:printClasspath').outcome == SUCCESS
//        result.task(':appTest:test').outcome == SUCCESS

//        result.standardOutput.contains('baseTest wins!') //TODO enable
    }

    /**
     * simulate depth-first traversal for top-level module 'appTest'
     * we want to order it like this: [gosu-core-api-1.9.1.jar, appExt.jar, app.jar, gw-asm-all-5.0.4.jar, baseTest.jar]
     * we'll do this by getting the list positions of app.jar and baseTest.jar and swapping them
     *
     * @return
     */
    def 'test depth-first traversal'() {
        given:
        appTestBuildScript <<
            """
            compileGosu {
                classpath = putAppAheadOfBaseTest()
            }

            def putAppAheadOfBaseTest() {
                FileCollection rearrangedClasspath // = []
                //outputs.files rearrangedClasspath

                List<File> defaultClasspath = configurations.compile.files.asList()
                println 'Got defaultClasspath: ' + defaultClasspath.collect { it.name }
                int baseTestPosition = defaultClasspath.findIndexOf { it.name.equals('baseTest.jar') }
                int appPosition = defaultClasspath.findIndexOf { it.name.equals('app.jar') }
                def tmp = defaultClasspath.getAt(baseTestPosition)
                defaultClasspath[baseTestPosition] = defaultClasspath.getAt(appPosition)
                defaultClasspath[appPosition] = tmp
                rearrangedClasspath = project.files(defaultClasspath)
                println 'Rearranged to ' + rearrangedClasspath.collect { it.name }
                return rearrangedClasspath
            }
            """
        
        when:
        GradleRunner runner = GradleRunner.create()
                .withProjectDir(rootFolder.root)
                .withPluginClasspath(pluginClasspath)
                .withArguments(':appTest:printClasspath', ':appTest:test', '-is')
        
        BuildResult result = runner.build()

        then:
        notThrown(UnexpectedBuildFailure)
        println(result.standardOutput)
        result.standardError.empty

        String[] expected = ['appExt.jar', 'app.jar', 'baseTest.jar']

        String[] orderedClasspath = getOrderedClasspath(result.standardOutput)
        
        println('orderedClasspath is: ' + orderedClasspath)

        //filter gosu dependencies not related to this test
        List<String> actual = orderedClasspath.findAll { expected.contains(it) }

        assertThat(actual).containsExactly(expected)

        result.task(':appTest:printClasspath').outcome == SUCCESS
//        result.task(':appTest:test').outcome == SUCCESS

//        result.standardOutput.contains('app wins!') //TODO enable
    }

    private static String[] getOrderedClasspath(String stdOut) {
        //get the line starting with 'The classpath is: '
        Matcher matcher = stdOut =~ /The classpath is: \[(.*)\]/
        matcher.find()
        matcher.groupCount() == 1
        return matcher.group(1).split(', ')
    }

}<commit_msg>#13 - Use the sourceSets closure to simulate the Guidewire-specific build requirements   * Actually compile and test something!   * We probably don't need an `orderClasspath` property; we can use the sourceSets closure instead   * It's reasonable to expect that setting the `testCompile` classpath would also be necessary in a real-world situation, if appExt and baseTest had differing APIs<commit_after>package org.gosulang.gradle.functional.classpath

import org.gosulang.gradle.functional.AbstractGosuPluginSpecification
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.GradleRunner
import org.gradle.testkit.runner.UnexpectedBuildFailure
import org.junit.rules.TemporaryFolder

import java.util.regex.Matcher

import static org.fest.assertions.Assertions.*
import static org.gradle.testkit.runner.TaskOutcome.*

/**
 * commons, base and app are three projects. On the file system, they are peers sitting at the same level on the tree
 */
class MultiModuleGraphTraversalTest extends AbstractGosuPluginSpecification {
    TemporaryFolder rootFolder
    File baseTestFolder
    File appFolder
    File appExtFolder
    File appTestFolder

    File rootProjectSettings

    File rootBuildScript
    File appExtBuildScript
    File appTestBuildScript

    File baseTestFoo
    File appFoo
    File fooTest

    /**
     * super#setup is invoked automatically
     * @return
     */
    @Override
    def setup() {
        rootFolder = testProjectDir
        baseTestFolder = rootFolder.newFolder('baseTest')
        appFolder = rootFolder.newFolder('app')
        appExtFolder = rootFolder.newFolder('appExt')
        appTestFolder = rootFolder.newFolder('appTest')
        
        rootProjectSettings = rootFolder.newFile('settings.gradle')
        
        rootBuildScript = buildScript //from superclass
        appExtBuildScript = new File(appExtFolder, 'build.gradle')
        appTestBuildScript = new File(appTestFolder, 'build.gradle')

        baseTestFoo = new File(rootFolder.newFolder('baseTest', 'src', 'main', 'gosu'), 'Foo.gs')
        appFoo = new File(rootFolder.newFolder('app', 'src', 'main', 'gosu'), 'Foo.gs')
        fooTest = new File(rootFolder.newFolder('appTest', 'src', 'test', 'gosu'), 'FooTest.gs')

        rootBuildScript <<
            """
            plugins {
                id 'org.gosu-lang.gosu'
            }

            allprojects {
                apply plugin: 'org.gosu-lang.gosu'
                repositories {
                    mavenLocal()
                    mavenCentral()
                    maven {
                        url 'https://oss.sonatype.org/content/repositories/snapshots' //for Gosu snapshot builds
                    }
                }
                dependencies {
                    compile 'org.gosu-lang.gosu:gosu-core-api:$gosuVersion'
                    testCompile 'junit:junit:4.12'
                }
                task printClasspath << {
                    println 'The classpath is: ' + compileGosu.classpath.files.collect { it.name } //configurations.compile.files.collect { it.name }
                }
            }
            """
        
        rootProjectSettings <<
            """
            include ':baseTest'
            include ':app'
            include ':appExt'
            include ':appTest'
            """

        appExtBuildScript << 
            """
            dependencies {
                compile project(':app')
            }
            """

        appTestBuildScript <<
            """
            dependencies {
                compile project(':appExt')
                compile project(':baseTest')
            }
            """

        baseTestFoo <<
            """
            class Foo {
                construct() {
                    print("baseTest wins!")
                }
            }
            """

        appFoo <<
            """
            class Foo {
                construct() {
                    print("app wins!")
                }
            }
            """

        fooTest <<
            """
            uses org.junit.Test

            class FooTest {

                @Test
                function whoWins() {
                    new Foo()  //later we'll check the output of this
                }
            }
            """
    }

    def 'test breadth-first traversal (Gradle default behavior)'() {
        when:
        GradleRunner runner = GradleRunner.create()
                .withProjectDir(rootFolder.root)
                .withPluginClasspath(pluginClasspath)
                .withArguments(':appTest:printClasspath', ':appTest:test', '-is')

        BuildResult result = runner.build()

        then:
        notThrown(UnexpectedBuildFailure)
        println(result.standardOutput)
        result.standardError.empty

        String[] expected = ['appExt.jar', 'baseTest.jar', 'app.jar']

        String[] orderedClasspath = getOrderedClasspath(result.standardOutput)

        println('orderedClasspath is: ' + orderedClasspath)

        //filter gosu dependencies not related to this test
        List<String> actual = orderedClasspath.findAll { expected.contains(it) }

        assertThat(actual).containsExactly(expected)

        result.task(':appTest:printClasspath').outcome == SUCCESS
        result.task(':appTest:test').outcome == SUCCESS

        result.standardOutput.contains('baseTest wins!')
        !result.standardOutput.contains('app wins!')
    }

    /**
     * simulate depth-first traversal for top-level module 'appTest'
     * we want to order it like this: [gosu-core-api-1.9.1.jar, appExt.jar, app.jar, gw-asm-all-5.0.4.jar, baseTest.jar]
     * we'll do this by getting the list positions of app.jar and baseTest.jar and swapping them
     *
     * @return
     */
    def 'test depth-first traversal'() {
        given:
        appTestBuildScript <<
            """
            def putAppAheadOfBaseTest(Configuration config) {
                FileCollection rearrangedClasspath // = []
                //outputs.files rearrangedClasspath

                List<File> defaultClasspath = config.files.asList()
                println 'Got defaultClasspath: ' + defaultClasspath.collect { it.name }
                int baseTestPosition = defaultClasspath.findIndexOf { it.name.equals('baseTest.jar') }
                int appPosition = defaultClasspath.findIndexOf { it.name.equals('app.jar') }
                def tmp = defaultClasspath.getAt(baseTestPosition)
                defaultClasspath[baseTestPosition] = defaultClasspath.getAt(appPosition)
                defaultClasspath[appPosition] = tmp
                rearrangedClasspath = project.files(defaultClasspath)
                println 'Rearranged to ' + rearrangedClasspath.collect { it.name }
                return rearrangedClasspath
            }

            sourceSets {
                main {
                    compileClasspath = putAppAheadOfBaseTest(configurations.compile)
                }
                test {
                   runtimeClasspath = putAppAheadOfBaseTest(configurations.testRuntime) + sourceSets.test.runtimeClasspath
                }
            }
            """
        
        when:
        GradleRunner runner = GradleRunner.create()
                .withProjectDir(rootFolder.root)
                .withPluginClasspath(pluginClasspath)
                .withArguments(':appTest:printClasspath', ':appTest:test', '-is')
        
        BuildResult result = runner.build()

        then:
        notThrown(UnexpectedBuildFailure)
        println(result.standardOutput)
        result.standardError.empty

        String[] expected = ['appExt.jar', 'app.jar', 'baseTest.jar']

        String[] orderedClasspath = getOrderedClasspath(result.standardOutput)
        
        println('orderedClasspath is: ' + orderedClasspath)

        //filter gosu dependencies not related to this test
        List<String> actual = orderedClasspath.findAll { expected.contains(it) }

        assertThat(actual).containsExactly(expected)

        result.task(':appTest:printClasspath').outcome == SUCCESS
        result.task(':appTest:test').outcome == SUCCESS

        result.standardOutput.contains('app wins!')
        !result.standardOutput.contains('baseTest wins!')
    }

    private static String[] getOrderedClasspath(String stdOut) {
        //get the line starting with 'The classpath is: '
        Matcher matcher = stdOut =~ /The classpath is: \[(.*)\]/
        matcher.find()
        matcher.groupCount() == 1
        return matcher.group(1).split(', ')
    }

}
{'fraction_non_alphanumeric': 0.09655172413793103, 'fraction_numerical': 0.001353528843055108, 'mean_word_length': 1.9826989619377162, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, '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': '6546036', 'n_tokens_mistral': 4180, 'n_tokens_neox': 3911, 'n_words': 834}