test {
    maxParallelForks = 1
    failFast true
    useJUnitPlatform {
        includeEngines 'junit-jupiter'
    }
}

project.test {
    def outputCache = new LinkedList<String>()

    beforeTest { TestDescriptor td -> outputCache.clear() }

    onOutput { TestDescriptor td, TestOutputEvent toe ->
        outputCache.add(toe.getMessage())
        while (outputCache.size() > 1000) outputCache.remove()
    }

    afterTest { TestDescriptor td, TestResult tr ->
        if (tr.resultType == TestResult.ResultType.FAILURE && outputCache.size() > 0) {
            println()
            println(" Output of ${td.className}.${td.name}:")
            outputCache.each { print(" > $it") }
        }
    }
}

tasks.withType(Test) {
    reports.html.enabled = false
    testLogging {
        events 'passed', 'failed', 'skipped'
        exceptionFormat = 'full'
        afterSuite { desc, result ->
            if (!desc.parent) {
                def color = { color, text ->
                    def colors = [black: 30, red: 31, green: 32, yellow: 33, blue: 34, magenta: 35, cyan: 36, white: 37]
                    return new String((char) 27) + "[${colors[color]}m${text}" + new String((char) 27) + "[0m"
                }
                def text = """\nResults: ${result.resultType} (${result.testCount} tests, ${result.successfulTestCount} successes, ${result.failedTestCount} failures, ${result.skippedTestCount} skipped)"""
                if (result.resultType == TestResult.ResultType.SUCCESS) println "${color 'green', text}"
                else println "${color 'red', text}"
            }
        }
    }
}

task unitTest(type: Test) {}
task integrationTest(type: Test) {}
task apiContractTest(type: Test) {}
task stubTest(type: Test) {}
task functionalTest(type: Test) {}
task allTest(type: Test) {}

project.afterEvaluate({

    // ALL TESTS EXCEPT FUNCTIONAL TESTS
    configure("test", [], ['functionalTest'])

    // ONLY UNIT TEST, CHANGE SET DETECTION SKIPPED
    configure("unitTest", null, ['integrationTest', 'contractTest', 'stubTest', 'functionalTest'])

    // ONLY INTEGRATION TEST, CHANGE SET DETECTION SKIPPED
    configure("integrationTest", ['integrationTest'], null)

    // ONLY CONTRACT TEST, CHANGE SET DETECTION SKIPPED
    configure("apiContractTest", ['contractTest'], null)

    // ONLY STUB TEST, CHANGE SET DETECTION SKIPPED
    configure("stubTest", ['stubTest'], null)

    // ONLY FUNCTIONAL TEST, CHANGE SET DETECTION SKIPPED
    configure("functionalTest", ['functionalTest'], null)

    // ALL TESTS EXCEPT FUNCTIONAL TESTS, CHANGE SET DETECTION SKIPPED
    configure("allTest", [], ['functionalTest'])
})

def configure(String taskName, includeJunit5Tags, excludeJunit5Tags) {
    tasks.named(taskName).configure {
        useJUnitPlatform {
            if (includeJunit5Tags) {
                if (includeJunit5Tags.size() == 0) includeTags "any()"
                else includeTags includeJunit5Tags as String[]
            }
            if (excludeJunit5Tags) {
                if (excludeJunit5Tags.size() == 0) excludeTags "any()"
                else excludeTags excludeJunit5Tags as String[]
            }
        }
    }
}

if (!project.hasProperty('skipInfraSetup')) {
    def infra_setup_up = rootProject.getTasksByName("infra_setup_up", false)[0]
    def infra_setup_down = rootProject.getTasksByName("infra_setup_down", false)[0]

    ['test', 'integrationTest', 'functionalTest', 'allTest'].each { task ->
        project."$task".dependsOn infra_setup_up
        infra_setup_down.mustRunAfter project."$task"
        project."$task".finalizedBy infra_setup_down
    }
}

if (project.tasks.findByName('generateContractTests')) {
    contractTest.dependsOn generateContractTests
    test.dependsOn generateContractTests
    allTest.dependsOn generateContractTests
}

if (project.tasks.findByName('verifierStubsJar')) {
    stubTest.dependsOn verifierStubsJar
}

