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

// Following Gradle best practices to keep build logic organized

// ----------------------------------------------------------------------------
// Functional testing harness creation. This helps run the cross-check tests.
// The Makefile precross target invokes the shadowJar task and the tests.json
// code is changed to call runclient or runserver as needed.

// ----------------------------------------------------------------------------
// Cross Test sources are separated in their own sourceSet
//
sourceSets {
    crossTest {
        java {
        }
    }
}

// see https://docs.gradle.org/current/userguide/java_library_plugin.html
// 1. defines cross test implementation that includes all test implementation, which in turn
//    contains all implementation dependencies
// 2. defines cross test runtime that further includes test runtime only dependencies
// 3. the cross test implementation will need to depends on main and test output
// 4. shadow jar will package both main and test source set, along with cross test runtime dependencies
configurations {
    crossTestImplementation {
        description "implementation for cross test"
        extendsFrom testImplementation
    }
    crossTestRuntime {
        description "runtime dependencies for cross test"
        extendsFrom crossTestImplementation, testRuntimeOnly
    }
}

dependencies {
    crossTestImplementation "org.apache.tomcat.embed:tomcat-embed-core:${tomcatEmbedVersion}"
    crossTestImplementation sourceSets.main.output
    crossTestImplementation sourceSets.test.output
}

// I am using shadow plugin to make a self contained functional test Uber JAR that
// eliminates startup problems with wrapping the cross-check harness in Gradle.
// This is used by the runner scripts as the single classpath entry which
// allows the process to be as lightweight as it can.
shadowJar {
    description = 'Assemble a test JAR file for cross-check execution'
    // make sure the runners are created when this runs
    dependsOn 'generateRunnerScriptForClient', 'generateRunnerScriptForNonblockingClient', 'generateRunnerScriptForServer', 'generateRunnerScriptForNonblockingServer', 'generateRunnerScriptForTServletServer'
    archiveBaseName.set('functionalTest')
    destinationDirectory = file("$buildDir/functionalTestJar")
    // We do not need a version number for this internal jar
    archiveVersion.set(null)
    // Bundle the complete set of unit test classes including generated code
    // and the runtime dependencies in one JAR to expedite execution.
    // see https://imperceptiblethoughts.com/shadow/custom-tasks/
    from sourceSets.test.output
    from sourceSets.crossTest.output
    configurations = [project.configurations.crossTestRuntime]
}

// Common script runner configuration elements
def scriptExt = ''
def execExt = ''
def scriptHead = '#!/bin/bash'
def args = '$*'

// Although this is marked internal it is an available and stable interface
if (org.gradle.internal.os.OperatingSystem.current().windows) {
    scriptExt = '.bat'
    execExt = '.exe'
    scriptHead = '@echo off'
    args = '%*'
}

// The Java executable to use with the runner scripts
def javaExe = file("${System.getProperty('java.home')}/bin/java${execExt}").canonicalPath
// The common Uber jar path
def jarPath = shadowJar.archiveFile.get().asFile.canonicalPath
def trustStore = file("${projectDir}/src/crossTest/resources/.truststore").canonicalPath
def serverKeyStore = file("${projectDir}/src/crossTest/resources/.serverkeystore").canonicalPath
def clientKeyStore = file("${projectDir}/src/crossTest/resources/.clientkeystore").canonicalPath

task generateRunnerScriptForClient(group: 'Build') {
    description = 'Generate a runner script for cross-check tests with TestClient'

    def clientFile = file("$buildDir/runclient${scriptExt}")

    def runClientText = """\
${scriptHead}

"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.keyStore=$clientKeyStore" -Djavax.net.ssl.keyStorePassword=thrift "-Djavax.net.ssl.trustStore=$trustStore" -Djavax.net.ssl.trustStorePassword=thrift org.apache.thrift.test.TestClient $args
"""
    inputs.property 'runClientText', runClientText
    outputs.file clientFile

    doLast {
        clientFile.parentFile.mkdirs()
        clientFile.text = runClientText
        clientFile.setExecutable(true, false)
    }
}

task generateRunnerScriptForNonblockingClient(group: 'Build') {
    description = 'Generate a runner script for cross-check tests with TestNonblockingClient'

    def clientFile = file("$buildDir/runnonblockingclient${scriptExt}")

    def runClientText = """\
${scriptHead}

"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.keyStore=$clientKeyStore" -Djavax.net.ssl.keyStorePassword=thrift "-Djavax.net.ssl.trustStore=$trustStore" -Djavax.net.ssl.trustStorePassword=thrift org.apache.thrift.test.TestNonblockingClient $args
"""
    inputs.property 'runClientText', runClientText
    outputs.file clientFile

    doLast {
        clientFile.parentFile.mkdirs()
        clientFile.text = runClientText
        clientFile.setExecutable(true, false)
    }
}

task generateRunnerScriptForServer(group: 'Build') {
    description = 'Generate a runner script for cross-check tests with TestServer'

    def serverFile = file("$buildDir/runserver${scriptExt}")

    def runServerText = """\
${scriptHead}

"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.keyStore=$serverKeyStore" -Djavax.net.ssl.keyStorePassword=thrift org.apache.thrift.test.TestServer $args
"""

    inputs.property 'runServerText', runServerText
    outputs.file serverFile

    doLast {
        serverFile.parentFile.mkdirs()
        serverFile.text = runServerText
        serverFile.setExecutable(true, false)
    }
}

task generateRunnerScriptForNonblockingServer(group: 'Build') {
    description = 'Generate a runner script for cross-check tests with TestNonblockingServer'

    def serverFile = file("$buildDir/runnonblockingserver${scriptExt}")

    def runServerText = """\
${scriptHead}

"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.keyStore=$serverKeyStore" -Djavax.net.ssl.keyStorePassword=thrift org.apache.thrift.test.TestNonblockingServer $args
"""

    inputs.property 'runServerText', runServerText
    outputs.file serverFile

    doLast {
        serverFile.parentFile.mkdirs()
        serverFile.text = runServerText
        serverFile.setExecutable(true, false)
    }
}

task generateRunnerScriptForTServletServer(group: 'Build') {
    description = 'Generate a runner script for cross-check tests with TestTServletServer'

    def serverFile = file("$buildDir/runservletserver${scriptExt}")

    def runServerText = """\
${scriptHead}

"${javaExe}" -cp "$jarPath" "-Djavax.net.ssl.keyStore=$serverKeyStore" -Djavax.net.ssl.keyStorePassword=thrift org.apache.thrift.test.TestTServletServer $args
"""

    inputs.property 'runServerText', runServerText
    outputs.file serverFile

    doLast {
        serverFile.parentFile.mkdirs()
        serverFile.text = runServerText
        serverFile.setExecutable(true, false)
    }
}
