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


plugins {
  id 'nebula.facet'
  id 'org.apache.geode.gradle.geode-build-tools'
}

import org.apache.geode.gradle.testing.repeat.RepeatTest
import org.apache.geode.gradle.plugins.TestPropertiesWriter

// This configuration might be safely removed once the CategoryWithParameterizedRunnerFactory
// and associated classes are themselves removed, per GEODE-5606
configurations {
  apt
}

compileTestJava {
  options.annotationProcessorPath = files(configurations['apt'])
}

test {
  doFirst {
    TestPropertiesWriter.writeTestProperties(buildDir, name)
  }
  if (project.hasProperty('testMaxParallelForks')) {
    maxParallelForks = Integer.parseUnsignedInt(project.testMaxParallelForks)
  } else {
    maxParallelForks = Runtime.runtime.availableProcessors().intdiv(2) ?: 1
  }
}


facets {
  integrationTest {
    includeInCheckLifecycle = false
  }
  distributedTest {
    includeInCheckLifecycle = false
  }
  performanceTest {
    includeInCheckLifecycle = false
  }
  acceptanceTest {
    includeInCheckLifecycle = false
  }
  uiTest {
    includeInCheckLifecycle = false
  }
  upgradeTest {
    includeInCheckLifecycle = false
  }
}

dependencies {
  // Do not add dependencies here that are not part of a custom configuration.
  // Dependencies should be explicit in the relevant build.gradle files to keep our modules
  //  as decoupled as possible.

  // This 'apt' configuration to be removed by GEODE-5606.
  apt files("${rootProject.projectDir}/buildSrc/build/libs/buildSrc.jar")
  apt('junit:junit:' + DependencyConstraints.get('junit.version')) {
    transitive = false
  }

  testRuntimeOnly('cglib:cglib:' + DependencyConstraints.get('cglib.version')) {
    exclude module: 'org.apache.ant'
  }
}

configure([integrationTest, distributedTest, performanceTest, acceptanceTest, uiTest,
           upgradeTest]) {
  forkEvery 1

  doFirst {
    TestPropertiesWriter.writeTestProperties(buildDir, name)
  }
  outputs.upToDateWhen { false }
}

if (project.hasProperty("forceTest")) {
  // All test facets already force rerun.  Only :test can be upToDate.
  test {
    outputs.upToDateWhen { false }
  }
}

tasks.register('repeatDistributedTest', RepeatTest) {
  dependsOn { distributedTest.taskDependencies }
  classpath = project.sourceSets.distributedTest.runtimeClasspath
  testClassesDirs = project.sourceSets.distributedTest.output.classesDirs
}

tasks.register('repeatIntegrationTest', RepeatTest) {
  dependsOn { integrationTest.taskDependencies }
  classpath = project.sourceSets.integrationTest.runtimeClasspath
  testClassesDirs = project.sourceSets.integrationTest.output.classesDirs
}

tasks.register('repeatAcceptanceTest', RepeatTest) {
  dependsOn { acceptanceTest.taskDependencies }
  classpath = project.sourceSets.acceptanceTest.runtimeClasspath
  testClassesDirs = project.sourceSets.acceptanceTest.output.classesDirs
}

tasks.register('repeatUpgradeTest', RepeatTest) {
  dependsOn { upgradeTest.taskDependencies }
  classpath = project.sourceSets.upgradeTest.runtimeClasspath
  testClassesDirs = project.sourceSets.upgradeTest.output.classesDirs
}

tasks.register('repeatUnitTest', RepeatTest) {
  dependsOn { test.taskDependencies }
  // default classpath works for this one.
}

configure([test, integrationTest, distributedTest, performanceTest, acceptanceTest, uiTest, upgradeTest,
           repeatDistributedTest, repeatIntegrationTest, repeatUpgradeTest, repeatUnitTest, repeatAcceptanceTest
]) {
  useJUnitPlatform()
}

configure([integrationTest, distributedTest, performanceTest, acceptanceTest, uiTest, upgradeTest]) {
  if (project.hasProperty('excludeTest')) {
    exclude project.getProperty('excludeTest').split(',')
  }
}

configure([repeatDistributedTest, repeatIntegrationTest, repeatUpgradeTest, repeatUnitTest, repeatAcceptanceTest]) {
  times = Integer.parseInt(repeat)
  forkEvery 1

  testFramework.options.excludeTags += "org.apache.geode.test.junit.categories.IgnoreInRepeatTestTasks"

  outputs.upToDateWhen { false }

  if (project.hasProperty("failOnNoMatchingTests")) {
    filter {
      setFailOnNoMatchingTests(Boolean.valueOf(project.failOnNoMatchingTests))
    }
  }
}

configure([integrationTest, distributedTest, performanceTest]) {
  if (project.hasProperty("testCategory")) {
    testFramework.options.includeTags += project.testCategory
  }
}

// apply common test configuration
gradle.taskGraph.whenReady({ graph ->
  tasks.withType(Test).each { test ->
    check.dependsOn test
    test.configure {
      onlyIf { !Boolean.getBoolean('skip.tests') }

      def resultsDir = TestPropertiesWriter.testResultsDir(buildDir, test.name)
      test.workingDir = resultsDir

      reports.html.destination = file "$buildDir/reports/$name"
      testLogging {
        exceptionFormat = 'full'
      }

      maxHeapSize '768m'
      jvmArgs += ['-XX:+HeapDumpOnOutOfMemoryError', '-ea']
      if (project.hasProperty('testJVMVer') && testJVMVer.toInteger() >= 9) {
        jvmArgs += [
                "--add-opens=java.base/java.io=ALL-UNNAMED",
                "--add-opens=java.base/java.lang=ALL-UNNAMED",
                "--add-opens=java.base/java.lang.annotation=ALL-UNNAMED",
                "--add-opens=java.base/java.lang.module=ALL-UNNAMED",
                "--add-opens=java.base/java.lang.ref=ALL-UNNAMED",
                "--add-opens=java.base/java.lang.reflect=ALL-UNNAMED",
                "--add-opens=java.base/java.math=ALL-UNNAMED",
                "--add-opens=java.base/java.net=ALL-UNNAMED",
                "--add-opens=java.base/java.nio=ALL-UNNAMED",
                "--add-opens=java.base/java.nio.channels=ALL-UNNAMED",
                "--add-opens=java.base/java.nio.channels.spi=ALL-UNNAMED",
                "--add-opens=java.base/java.nio.charset=ALL-UNNAMED",
                "--add-opens=java.base/java.nio.file.attribute=ALL-UNNAMED",
                "--add-opens=java.base/java.nio.file.spi=ALL-UNNAMED",
                "--add-opens=java.base/java.security=ALL-UNNAMED",
                "--add-opens=java.base/java.text=ALL-UNNAMED",
                "--add-opens=java.base/java.time=ALL-UNNAMED",
                "--add-opens=java.base/java.time.chrono=ALL-UNNAMED",
                "--add-opens=java.base/java.time.format=ALL-UNNAMED",
                "--add-opens=java.base/java.time.temporal=ALL-UNNAMED",
                "--add-opens=java.base/java.time.zone=ALL-UNNAMED",
                "--add-opens=java.base/java.util=ALL-UNNAMED",
                "--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
                "--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED",
                "--add-opens=java.base/java.util.concurrent.locks=ALL-UNNAMED",
                "--add-opens=java.base/java.util.jar=ALL-UNNAMED",
                "--add-opens=java.base/java.util.regex=ALL-UNNAMED",
                "--add-opens=java.base/java.util.zip=ALL-UNNAMED",
                "--add-opens=java.base/javax.net.ssl=ALL-UNNAMED",
                "--add-opens=java.base/jdk.internal.loader=ALL-UNNAMED",
                "--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED",
                "--add-opens=java.base/jdk.internal.module=ALL-UNNAMED",
                "--add-opens=java.base/jdk.internal.platform=ALL-UNNAMED",
                "--add-opens=java.base/jdk.internal.ref=ALL-UNNAMED",
                "--add-opens=java.base/jdk.internal.reflect=ALL-UNNAMED",
                "--add-opens=java.base/jdk.internal.util.jar=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.annotation=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.generics.factory=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.generics.repository=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.generics.scope=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.generics.tree=ALL-UNNAMED",
                "--add-opens=java.base/sun.net.www=ALL-UNNAMED",
                "--add-opens=java.base/sun.net.www.protocol.file=ALL-UNNAMED",
                "--add-opens=java.base/sun.net.www.protocol.jar=ALL-UNNAMED",
                "--add-opens=java.base/sun.nio.ch=ALL-UNNAMED",
                "--add-opens=java.base/sun.nio.cs=ALL-UNNAMED",
                "--add-opens=java.base/sun.nio.fs=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.generics.factory=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.generics.reflectiveObjects=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.generics.repository=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.generics.scope=ALL-UNNAMED",
                "--add-opens=java.base/sun.reflect.generics.tree=ALL-UNNAMED",
                "--add-opens=java.base/sun.security.provider=ALL-UNNAMED",
                "--add-opens=java.base/sun.security.ssl=ALL-UNNAMED",
                "--add-opens=java.base/sun.security.util=ALL-UNNAMED",
                "--add-opens=java.base/sun.util.calendar=ALL-UNNAMED",
                "--add-opens=java.base/sun.util.locale=ALL-UNNAMED",
                "--add-opens=java.logging/java.util.logging=ALL-UNNAMED",
                "--add-opens=java.management/javax.management=ALL-UNNAMED",
                "--add-opens=java.management/javax.management.openmbean=ALL-UNNAMED",
                "--add-opens=java.management/sun.management=ALL-UNNAMED",
                "--add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED",
                "--add-opens=java.xml/jdk.xml.internal=ALL-UNNAMED",
                "--add-opens=jdk.management/com.sun.management.internal=ALL-UNNAMED",

                "--add-exports=java.base/sun.security.x509=ALL-UNNAMED",
                "--add-exports=java.management/com.sun.jmx.remote.security=ALL-UNNAMED",
        ]

        if (System.getProperty("os.name").startsWith('Linux')) {
          jvmArgs += "--add-opens=java.base/jdk.internal.platform=ALL-UNNAMED"
          jvmArgs += "--add-opens=java.base/jdk.internal.platform.cgroupv1=ALL-UNNAMED"
        }
        if(testJVMVer.toInteger() >= 17) {
          jvmArgs += "--add-exports=java.base/jdk.internal.util.random=ALL-UNNAMED"
        }
      }
      if (project.hasProperty('testJVM') && !testJVM.trim().isEmpty()) {
        executable = "${testJVM}/bin/java"
      }

      def basePort = 'dunit.debug.basePort'
      if (project.hasProperty(basePort)) {
        systemProperty basePort, project.getProperties()[basePort]
      }

      def suspendVM = 'dunit.debug.suspendVM'
      if (project.hasProperty(suspendVM)) {
        systemProperty suspendVM, project.getProperties()[suspendVM]
      }

      systemProperty 'gemfire.DEFAULT_MAX_OPLOG_SIZE', '10'
      systemProperty 'gemfire.disallowMcastDefaults', 'true'
      systemProperty 'jline.terminal', 'jline.UnsupportedTerminal'
      def logLevel = System.getProperty('log-level')
      if (logLevel != null) {
        systemProperty 'log-level', logLevel
      }
      def log4jLocation = System.getProperty('log4j.configurationFile')
      if (log4jLocation != null) {
        systemProperty 'log4j.configurationFile', log4jLocation
      }

      // The distributed tests seem to need to use /tmp directly,
      // so exclude them from using the supplied temp directory.
      if (!test.name.contains("distributed") && !test.name.contains("repeatDistributed")) {
        systemProperty 'java.io.tmpdir', System.getProperty('java.io.tmpdir')
      }

      def eol = System.lineSeparator()
      def progress = new File(resultsDir, "$test.name-progress.txt")
      beforeTest { desc ->
        def now = new Date().format('yyyy-MM-dd HH:mm:ss.SSS Z')
        progress << "$now Starting test $desc.className $desc.name$eol"
      }
      afterTest { desc, result ->
        def now = new Date().format('yyyy-MM-dd HH:mm:ss.SSS Z')
        progress << "$now Completed test $desc.className $desc.name with result: ${result.resultType}$eol"
      }

      doFirst {
        resultsDir.deleteDir()
        resultsDir.mkdirs()
      }
    }
  }
  repeatAcceptanceTest.exclude acceptanceTest.excludes
  repeatDistributedTest.exclude distributedTest.excludes
  repeatIntegrationTest.exclude integrationTest.excludes
  repeatUnitTest.exclude test.excludes
  repeatUpgradeTest.exclude upgradeTest.excludes
})

acceptanceTest {
  // Acceptance tests may reach out and run a gfsh command from the assembled gfsh.
  // If the test JVM version is specified, we must have the correct JAVA_HOME set for gfsh to use.
  // See also environment configuration for parallel testing in multi-process-test.gradle
  if (project.hasProperty('testJVM') && !testJVM.trim().isEmpty()) {
    environment "JAVA_HOME", "${project.testJVM}"
  }
}


// Make precheckin task run all validation tests for checking in code.
task precheckin(dependsOn: [build, acceptanceTest, integrationTest, distributedTest, upgradeTest]) {
  description 'Run this task before checking in code to validate changes. It runs tests beyond unitTest'
}

tasks.withType(Test).plus([build, check]).each {
  it.finalizedBy rootProject.tasks.combineReports
  rootProject.tasks.combineReports.mustRunAfter it
}

project.tasks.named('assemble').configure {
  // This is a semi-lazy hook so that our test sources are compiled most of the time.
  dependsOn(
    [
      project.tasks.named('compileTestJava'),
      project.tasks.named('compileIntegrationTestJava'),
      project.tasks.named('compileDistributedTestJava'),
      project.tasks.named('compileAcceptanceTestJava'),
      project.tasks.named('compileUiTestJava'),
      project.tasks.named('compilePerformanceTestJava'),
      project.tasks.named('compileUpgradeTestJava'),
    ]
  )
}
