/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.
 */

import org.apache.groovy.gradle.ConcurrentExecutionControlBuildService

// TODO: Instead of adding to the test sources, these should be a
// separate source set so that we can run spec tests in isolation
sourceSets {
    test {
        groovy {
            srcDirs += 'src/spec/test'
        }
        resources {
            srcDirs += 'src/spec/test-resources'
        }
    }
}

dependencies {
    testCompileOnly "junit:junit:${versions.junit}"
    testImplementation "org.junit.jupiter:junit-jupiter:${versions.junit5}"
    testRuntimeOnly "org.junit.vintage:junit-vintage-engine:${versions.junit5}"
    testRuntimeOnly "org.junit.platform:junit-platform-launcher:${versions.junit5Platform}", {
        exclude group: 'org.apiguardian', module: 'apiguardian-api'
    }
}

tasks.withType(Test).configureEach {
    def fs = objects.newInstance(TestServices).fileSystemOperations
    def grapeDirectory = new File(temporaryDir, 'grape')
    def jdk8 = ['-XX:+UseConcMarkSweepGC']
    def jdk9 = ['-Djava.locale.providers=COMPAT,SPI']
    def common = ['-ea', "-Xms${groovyJUnit_ms}", "-Xmx${groovyJUnit_mx}", '-Duser.language=en']
    if (JavaVersion.current().isJava9Compatible()) {
        jvmArgs(*common, *jdk9)
        systemProperty 'groovy.force.illegal.access', findProperty('groovy.force.illegal.access')
    } else {
        jvmArgs(*common, *jdk8)
    }
    def testdb = System.properties['groovy.testdb.props']
    if (testdb) {
        systemProperty 'groovy.testdb.props', testdb
    }
    def headless = System.properties['java.awt.headless']
    if (headless == 'true') {
        systemProperty 'java.awt.headless', 'true'
    }
    systemProperty 'apple.awt.UIElement', 'true'
    systemProperty 'javadocAssertion.src.dir', './src/main'

    jvmArgumentProviders.add(new TestCommandLineArgumentProvider(
        grapeRoot: grapeDirectory,
        gradleHome: gradle.gradleHomeDir, // this is needed by the security.policy
        userHome: temporaryDir // make sure tests are isolated from real user home or tests using Grape may fail
    ))

    if (rootProject.hasProperty('target.java.home')) {
        String targetJavaHome = rootProject.property('target.java.home')?.trim()
        if (targetJavaHome) {
            executable = "${targetJavaHome}/bin/java"
            println "Using ${executable} to run tests"
        }
    }

    forkEvery = 50
    maxParallelForks = sharedConfiguration.isRunningOnCI ? 1 : (Runtime.runtime.availableProcessors().intdiv(2) ?: 1)
    scanForTestClasses = true
    ignoreFailures = false
    classpath = files('src/test/groovy') + classpath
    exclude buildExcludeFilter(it.name == 'test')
    ext.resultText = ''

    testLogging {
        exceptionFormat = 'full'
        events 'FAILED'
        info.events 'FAILED', 'PASSED', 'SKIPPED'
    }

    useJUnitPlatform()
    usesService(ConcurrentExecutionControlBuildService.restrict(Test, gradle, 2))

    doFirst {
        fs.delete {
            // delete if it exists already to be in a clean state
            delete(grapeDirectory)
        }
    }

    doLast {
        fs.delete {
            delete(files(".").filter { it.name.endsWith '.class' })
        }
    }
}

class TestCommandLineArgumentProvider implements CommandLineArgumentProvider {

    @Internal
    File grapeRoot

    @Internal
    File gradleHome

    @Internal
    File userHome

    @Override
    Iterable<String> asArguments() {
        ["-Dgrape.root=${grapeRoot.absolutePath}".toString(),
         "-Dgradle.home=${gradleHome.absolutePath}".toString(),
         "-Duser.home=${userHome.absolutePath}".toString()]
    }
}

tasks.addRule('Pattern: testSingle<Name> will test **/<Name>.class') { String taskName ->
    if (taskName.startsWith('testSingle')) {
        tasks.register(taskName) { dependsOn(test) }
        test.includes = ['**/' + taskName.substring(10) + '.class']
        test.outputs.upToDateWhen { false }
    }
}

Closure buildExcludeFilter(boolean legacyTestSuite) {
    def excludes = []

    // if no network available, disable Grapes
    if (!providers.systemProperty('junit.network').getOrNull()) {
        excludes << 'groovy/grape/'
    }

    return { f ->
        excludes.any { f.file =~ it }
    }
}

interface TestServices {
    @javax.inject.Inject
    FileSystemOperations getFileSystemOperations()
}
