mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[build] Remove obsolete intellij.build.test.batchTest.includes (aka batch mode) in tests (IJI-3451)
GitOrigin-RevId: 0b6d98a14800c02ae357ae448335dae04ac396ba
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f11494e117
commit
6fdf0d02ea
@@ -153,14 +153,6 @@ open class TestingOptions {
|
||||
*/
|
||||
var isEnableCausalProfiling: Boolean = getBooleanProperty("intellij.build.test.enable.causal.profiling", false)
|
||||
|
||||
/**
|
||||
* Pattern to match tests in [mainModule] or default main module tests compilation outputs.
|
||||
* Tests from each matched class will be executed in a forked Runtime.
|
||||
*
|
||||
* E.g. "com/intellij/util/ui/standalone/ **Test.class"
|
||||
*/
|
||||
var batchTestIncludes: String? = System.getProperty("intellij.build.test.batchTest.includes")
|
||||
|
||||
/**
|
||||
* Run only whole classes/packages in forked Runtime
|
||||
* Allowed values:
|
||||
|
||||
+247
-420
@@ -10,7 +10,6 @@ import com.intellij.idea.IJIgnore
|
||||
import com.intellij.openapi.application.PathManager
|
||||
import com.intellij.openapi.util.Pair
|
||||
import com.intellij.openapi.util.SystemInfoRt
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import com.intellij.openapi.util.io.NioFiles
|
||||
import com.intellij.openapi.util.text.StringUtilRt
|
||||
@@ -19,7 +18,6 @@ import com.intellij.platform.ijent.community.buildConstants.MULTI_ROUTING_FILE_S
|
||||
import com.intellij.testFramework.SkipInHeadlessEnvironment
|
||||
import com.intellij.util.io.awaitExit
|
||||
import com.intellij.util.lang.UrlClassLoader
|
||||
import io.opentelemetry.api.common.AttributeKey
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
@@ -39,8 +37,6 @@ import org.jetbrains.intellij.build.causal.CausalProfilingOptions
|
||||
import org.jetbrains.intellij.build.dependencies.TeamCityHelper
|
||||
import org.jetbrains.intellij.build.impl.coverage.Coverage
|
||||
import org.jetbrains.intellij.build.impl.coverage.CoverageImpl
|
||||
import org.jetbrains.intellij.build.io.ZipEntryProcessorResult
|
||||
import org.jetbrains.intellij.build.io.readZipFile
|
||||
import org.jetbrains.intellij.build.io.runProcess
|
||||
import org.jetbrains.intellij.build.telemetry.TraceManager.spanBuilder
|
||||
import org.jetbrains.intellij.build.telemetry.block
|
||||
@@ -59,13 +55,11 @@ import java.nio.charset.Charset
|
||||
import java.nio.file.AccessDeniedException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.regex.Pattern
|
||||
import kotlin.io.path.ExperimentalPathApi
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.io.path.copyTo
|
||||
import kotlin.io.path.deleteRecursively
|
||||
import kotlin.io.path.exists
|
||||
import kotlin.io.path.extension
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.io.path.outputStream
|
||||
import kotlin.io.path.pathString
|
||||
@@ -261,11 +255,6 @@ internal class TestingTasksImpl(context: CompilationContext, private val options
|
||||
else if (options.testPatterns != null && options.testGroups != TestingOptions.ALL_EXCLUDE_DEFINED_GROUP) {
|
||||
warnOptionIgnored("intellij.build.test.patterns", "intellij.build.test.groups")
|
||||
}
|
||||
if (options.batchTestIncludes != null && !isRunningInBatchMode) {
|
||||
context.messages.warning(
|
||||
"'intellij.build.test.batchTest.includes' option will be ignored as other tests matching options are specified."
|
||||
)
|
||||
}
|
||||
|
||||
if (options.validateMainModule && mainModule.isNullOrEmpty()) {
|
||||
context.messages.logErrorAndThrow("'intellij.build.test.main.module.validate' option requires 'intellij.build.test.main.module' to be set")
|
||||
@@ -459,7 +448,7 @@ internal class TestingTasksImpl(context: CompilationContext, private val options
|
||||
}
|
||||
}
|
||||
|
||||
if (isBootstrapSuiteDefault && !isRunningInBatchMode) {
|
||||
if (isBootstrapSuiteDefault) {
|
||||
//module with "com.intellij.TestAll" which output should be found in `testClasspath + modulePath`
|
||||
val testFrameworkCoreModule = outputProvider.findRequiredModule("intellij.platform.testFramework.core")
|
||||
val testFrameworkCoreModuleOutputRoots = outputProvider.getModuleOutputRoots(testFrameworkCoreModule)
|
||||
@@ -508,10 +497,7 @@ internal class TestingTasksImpl(context: CompilationContext, private val options
|
||||
val allJvmArgs = ArrayList(jvmArgs)
|
||||
prepareEnvForTestRun(jvmArgs = allJvmArgs, systemProperties = systemProperties, classPath = bootstrapClasspath, remoteDebugging = remoteDebugging)
|
||||
val messages = context.messages
|
||||
if (isRunningInBatchMode) {
|
||||
messages.info("Running tests from $mainModule matched by '${options.batchTestIncludes}' pattern.")
|
||||
}
|
||||
else if (!testPatterns.isNullOrEmpty()) {
|
||||
if (!testPatterns.isNullOrEmpty()) {
|
||||
messages.info("Starting tests from patterns '${testPatterns}' from classpath of module '${mainModule}'")
|
||||
}
|
||||
else {
|
||||
@@ -790,140 +776,6 @@ internal class TestingTasksImpl(context: CompilationContext, private val options
|
||||
}.flatMap { it.getCompleted() }
|
||||
}
|
||||
|
||||
private fun getTestClassesForModule(mainModule: String, filteringPattern: Pattern = Pattern.compile(".*\\.class")): List<String> {
|
||||
val testClasses: List<String> = context.outputProvider.getModuleOutputRoots(context.outputProvider.findRequiredModule(mainModule), forTests = true).flatMap { root ->
|
||||
if (root.isRegularFile() && root.extension == "jar") {
|
||||
val classes = ArrayList<String>()
|
||||
val regex = filteringPattern.toRegex()
|
||||
readZipFile(root) { name, _ ->
|
||||
if (FileUtilRt.toSystemIndependentName(name).matches(regex)) {
|
||||
classes.add(name)
|
||||
}
|
||||
ZipEntryProcessorResult.CONTINUE
|
||||
}
|
||||
classes
|
||||
}
|
||||
else {
|
||||
Files.walk(root).use { stream ->
|
||||
stream.map { FileUtilRt.toSystemIndependentName(root.relativize(it).toString()) }.filter {
|
||||
filteringPattern.matcher(it).matches()
|
||||
}.toList()
|
||||
} ?: listOf()
|
||||
}
|
||||
}
|
||||
|
||||
if (testClasses.isEmpty()) {
|
||||
throw RuntimeException("No tests were found in module '$mainModule' with $filteringPattern")
|
||||
}
|
||||
|
||||
return testClasses
|
||||
}
|
||||
|
||||
private suspend fun runInBatchMode(
|
||||
mainModule: String,
|
||||
systemProperties: Map<String, String>,
|
||||
jvmArgs: List<String>,
|
||||
envVariables: Map<String, String>,
|
||||
bootstrapClasspath: List<String>,
|
||||
testClasspath: List<String>,
|
||||
devBuildServerSettings: DevBuildServerSettings?,
|
||||
) {
|
||||
val pattern = Pattern.compile(FileUtil.convertAntToRegexp(options.batchTestIncludes!!))
|
||||
val testClasses = getTestClassesForModule(mainModule = mainModule, filteringPattern = pattern)
|
||||
|
||||
val files = testClasspath.map { Path.of(it) }
|
||||
val loader = UrlClassLoader.build().files(files).get()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val testAnnotation4 = loader.loadClass("org.junit.Test") as Class<Annotation>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val testAnnotation5 = loader.loadClass("org.junit.jupiter.api.Test") as Class<Annotation>
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val testFactoryAnnotation5 = loader.loadClass("org.junit.jupiter.api.TestFactory") as Class<Annotation>
|
||||
|
||||
var noTestsInAllClasses = true
|
||||
for (testClass in testClasses) {
|
||||
val qName = FileUtilRt.getNameWithoutExtension(testClass).replace('/', '.')
|
||||
try {
|
||||
var noTests = true
|
||||
val aClass = loader.loadClass(qName)
|
||||
|
||||
val jUnit4And5TestMethods = getAnnotatedTestMethods(aClass, testAnnotation4, testAnnotation5, testFactoryAnnotation5)
|
||||
|
||||
// Run JUnit 4 and 5 whole test classes separately
|
||||
if (options.isDedicatedTestRuntime != "false" && jUnit4And5TestMethods.isNotEmpty()) {
|
||||
val exitCode = runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties,
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = null,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = qName,
|
||||
methodName = null,
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
noTests = exitCode == NO_TESTS_ERROR
|
||||
}
|
||||
// Run JUnit 4 and 5 test methods separately if any
|
||||
else if (jUnit4And5TestMethods.isNotEmpty()) {
|
||||
for (method in jUnit4And5TestMethods) {
|
||||
val exitCode = runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties,
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = null,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = qName,
|
||||
methodName = method,
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
noTests = noTests && exitCode == NO_TESTS_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to running whole class (JUnit 3+4)
|
||||
if (noTests) {
|
||||
val exitCode = runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties,
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = null,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = qName,
|
||||
methodName = null,
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
noTests = exitCode == NO_TESTS_ERROR
|
||||
}
|
||||
noTestsInAllClasses = noTestsInAllClasses && noTests
|
||||
}
|
||||
catch (e: Throwable) {
|
||||
throw RuntimeException("Failed to process $qName", e)
|
||||
}
|
||||
}
|
||||
|
||||
if (noTestsInAllClasses) {
|
||||
throw RuntimeException("No tests were found in $mainModule with $pattern")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAnnotatedTestMethods(aClass: Class<*>, vararg annotations: Class<Annotation>): List<String> {
|
||||
return aClass.methods
|
||||
.asSequence()
|
||||
.filter { m -> Modifier.isPublic(m.modifiers) }
|
||||
.filter { m -> annotations.any { a -> m.isAnnotationPresent(a) } }
|
||||
.map { m -> m.name }
|
||||
.toList()
|
||||
}
|
||||
|
||||
private suspend fun runJUnit5Engine(
|
||||
mainModule: String,
|
||||
systemProperties: Map<String, String>,
|
||||
@@ -934,291 +786,274 @@ internal class TestingTasksImpl(context: CompilationContext, private val options
|
||||
testClasspath: List<String>,
|
||||
devBuildServerSettings: DevBuildServerSettings?,
|
||||
) {
|
||||
if (isRunningInBatchMode) {
|
||||
spanBuilder("run tests in batch mode")
|
||||
.setAttribute(AttributeKey.stringKey("pattern"), options.batchTestIncludes ?: "")
|
||||
.use {
|
||||
runInBatchMode(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties,
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
testClasspath = testClasspath,
|
||||
devBuildServerSettings = devBuildServerSettings,
|
||||
)
|
||||
val messages = context.messages
|
||||
if (options.isDedicatedTestRuntime != "false") {
|
||||
if (options.isDedicatedTestRuntime != "class" && options.isDedicatedTestRuntime != "package") {
|
||||
messages.logErrorAndThrow("Unsupported 'intellij.build.test.dedicated.runtime' value: ${options.isDedicatedTestRuntime}. Expected 'class', 'package' or 'false'")
|
||||
}
|
||||
messages.info("Will run tests in dedicated runtimes ('${options.isDedicatedTestRuntime}')")
|
||||
// First, collect all tests for both JUnit5 and JUnit3+4
|
||||
val testClassesJUnit5 = spanBuilder("collect junit 5 tests").use {
|
||||
if (options.shouldSkipJUnit5Tests) {
|
||||
messages.warning("JUnit 5 tests collections is skipped")
|
||||
return@use emptyList()
|
||||
}
|
||||
|
||||
val testClassesListFile = Files.createTempFile("tests-to-run-", ".list").apply { Files.delete(this) }
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + ("intellij.build.test.list.classes" to testClassesListFile.absolutePathString()),
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = null,
|
||||
methodName = null,
|
||||
devBuildSettings = null,
|
||||
)
|
||||
testClassesListFile.let { if (Files.exists(it)) it.readLines() else emptyList() }
|
||||
}
|
||||
|
||||
val testClassesJUnit34 = block("collect junit 3+4 tests") {
|
||||
if (options.shouldSkipJUnit34Tests) {
|
||||
messages.warning("JUnit 3+4 tests collections is skipped")
|
||||
return@block emptyList()
|
||||
}
|
||||
|
||||
val testClassesListFile = Files.createTempFile("tests-to-run-", ".list").apply { Files.delete(this) }
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + ("intellij.build.test.list.classes" to testClassesListFile.absolutePathString()),
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = options.bootstrapSuite,
|
||||
methodName = null,
|
||||
devBuildSettings = null,
|
||||
)
|
||||
return@block testClassesListFile.let { if (Files.exists(it)) it.readLines() else emptyList() }
|
||||
}
|
||||
|
||||
if (testClassesJUnit5.isEmpty() && testClassesJUnit34.isEmpty() &&
|
||||
// a bucket might be empty for run configurations with too few tests due to imperfect tests balancing
|
||||
options.bucketsCount < 2) {
|
||||
throw NoTestsFound()
|
||||
}
|
||||
|
||||
if (options.isDedicatedTestRuntime == "class") {
|
||||
var hasFailures = false
|
||||
|
||||
suspend fun runOneClass(testClassName: String) {
|
||||
val exitCode = block("running test class '$testClassName'") {
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + ("idea.performance.tests.discovery.filter" to "true"),
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = testClassName,
|
||||
methodName = null,
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
}
|
||||
if (exitCode == 1) hasFailures = true // reported as test failure or assertNoUnhandledExceptions if exception
|
||||
else if (exitCode == NO_TESTS_ERROR) throw NoTestsFound()
|
||||
else if (exitCode != 0) throw RuntimeException("Unexpected exit code $exitCode when running tests in dedicated runtime (class mode)")
|
||||
}
|
||||
|
||||
if (testClassesJUnit5.isNotEmpty()) {
|
||||
messages.info("Will run JUnit 5 tests:\n${testClassesJUnit5.joinToString("\n")}")
|
||||
for (s in testClassesJUnit5) {
|
||||
runOneClass(s)
|
||||
}
|
||||
}
|
||||
if (testClassesJUnit34.isNotEmpty()) {
|
||||
messages.info("Will run JUnit 3+4 tests:\n${testClassesJUnit34.joinToString("\n")}")
|
||||
for (s in testClassesJUnit34) {
|
||||
runOneClass(s)
|
||||
}
|
||||
}
|
||||
|
||||
// On TeamCity test failures themselves control the build status, no need to report them as additional errors
|
||||
if (hasFailures && !TeamCityHelper.isUnderTeamCity) {
|
||||
throw RuntimeException("Tests failed in dedicated runtime (class mode)")
|
||||
}
|
||||
}
|
||||
else if (options.isDedicatedTestRuntime == "package") {
|
||||
var hasFailures = false
|
||||
|
||||
fun groupByPackages(tests: List<String>): Map<String, List<String>> {
|
||||
return tests.groupBy {
|
||||
val i = it.lastIndexOf('.')
|
||||
if (i != -1) it.substring(0, i) else ""
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun runOnePackage(entry: Map.Entry<String, List<String>>) {
|
||||
val packageName = entry.key
|
||||
val classes = entry.value
|
||||
|
||||
val exitCode = block("running tests in package '$packageName'") {
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + ("idea.performance.tests.discovery.filter" to "true"),
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = "__classes__",
|
||||
methodName = classes.joinToString(";"),
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
}
|
||||
if (exitCode == 1) hasFailures = true // reported as test failure or assertNoUnhandledExceptions if exception
|
||||
else if (exitCode == NO_TESTS_ERROR) throw NoTestsFound()
|
||||
else if (exitCode != 0) throw RuntimeException("Unexpected exit code $exitCode when running tests in dedicated runtime (package mode)")
|
||||
}
|
||||
|
||||
if (testClassesJUnit5.isNotEmpty()) {
|
||||
val packages = groupByPackages(testClassesJUnit5)
|
||||
messages.info(packages.entries.joinToString(prefix = "Will run JUnit 5 packages:\n", separator = "\n") { e ->
|
||||
e.value.joinToString(prefix = "${e.key}\n ", separator = "\n ")
|
||||
})
|
||||
for (entry in packages) {
|
||||
runOnePackage(entry)
|
||||
}
|
||||
}
|
||||
if (testClassesJUnit34.isNotEmpty()) {
|
||||
val packages = groupByPackages(testClassesJUnit34)
|
||||
messages.info(packages.entries.joinToString(prefix = "Will run JUnit 3+4 packages:\n", separator = "\n") { e ->
|
||||
e.value.joinToString(prefix = "${e.key}\n ", separator = "\n ")
|
||||
})
|
||||
for (entry in packages) {
|
||||
runOnePackage(entry)
|
||||
}
|
||||
}
|
||||
|
||||
// On TeamCity test failures themselves control the build status, no need to report them as additional errors
|
||||
if (hasFailures && !TeamCityHelper.isUnderTeamCity) {
|
||||
throw RuntimeException("Tests failed in dedicated runtime (package mode)")
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
val messages = context.messages
|
||||
if (options.isDedicatedTestRuntime != "false") {
|
||||
if (options.isDedicatedTestRuntime != "class" && options.isDedicatedTestRuntime != "package") {
|
||||
messages.logErrorAndThrow("Unsupported 'intellij.build.test.dedicated.runtime' value: ${options.isDedicatedTestRuntime}. Expected 'class', 'package' or 'false'")
|
||||
val failedClassesJUnit5List = Files.createTempFile("failed-classes-junit5-", ".list").apply { Files.delete(this) }
|
||||
val failedClassesJUnit34List = Files.createTempFile("failed-classes-junit34-", ".list").apply { Files.delete(this) }
|
||||
val additionalPropertiesJUnit5: Map<String, String> = failedClassesJUnit5List.let {
|
||||
if (options.attemptCount > 1) mapOf("intellij.build.test.retries.failedClasses.file" to "$it", "intellij.build.test.list.file" to "$it")
|
||||
else emptyMap()
|
||||
}
|
||||
val additionalPropertiesJUnit34: Map<String, String> = failedClassesJUnit34List.let {
|
||||
if (options.attemptCount > 1) mapOf("intellij.build.test.retries.failedClasses.file" to "$it", "intellij.build.test.list.file" to "$it")
|
||||
else emptyMap()
|
||||
}
|
||||
var runJUnit5 = !options.shouldSkipJUnit5Tests
|
||||
var runJUnit34 = !options.shouldSkipJUnit34Tests
|
||||
var lastExitCode5 = 0
|
||||
var lastExitCode34 = 0
|
||||
for (attempt in 1..options.attemptCount) {
|
||||
if (!runJUnit5 && !runJUnit34) break
|
||||
val spanNameSuffix = if (options.attemptCount > 1) " (attempt $attempt)" else ""
|
||||
val systemPath = systemProperties[PathManager.PROPERTY_SYSTEM_PATH]
|
||||
|
||||
val additionalProperties = mutableMapOf(PathManager.PROPERTY_LOG_PATH to "$systemPath${File.separator}log${File.separator}junit5")
|
||||
if (attempt > 1) {
|
||||
additionalProperties["intellij.build.test.ignoreFirstAndLastTests"] = "true"
|
||||
}
|
||||
messages.info("Will run tests in dedicated runtimes ('${options.isDedicatedTestRuntime}')")
|
||||
// First, collect all tests for both JUnit5 and JUnit3+4
|
||||
val testClassesJUnit5 = spanBuilder("collect junit 5 tests").use {
|
||||
if (options.shouldSkipJUnit5Tests) {
|
||||
messages.warning("JUnit 5 tests collections is skipped")
|
||||
return@use emptyList()
|
||||
|
||||
val exitCode5: Int = if (runJUnit5) {
|
||||
block("run junit 5 tests${spanNameSuffix}") {
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + additionalProperties + additionalPropertiesJUnit5,
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = null,
|
||||
methodName = null,
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
}
|
||||
|
||||
val testClassesListFile = Files.createTempFile("tests-to-run-", ".list").apply { Files.delete(this) }
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + ("intellij.build.test.list.classes" to testClassesListFile.absolutePathString()),
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = null,
|
||||
methodName = null,
|
||||
devBuildSettings = null,
|
||||
)
|
||||
testClassesListFile.let { if (Files.exists(it)) it.readLines() else emptyList() }
|
||||
}
|
||||
else {
|
||||
0
|
||||
}
|
||||
if (runJUnit5) lastExitCode5 = exitCode5
|
||||
|
||||
val testClassesJUnit34 = block("collect junit 3+4 tests") {
|
||||
if (options.shouldSkipJUnit34Tests) {
|
||||
messages.warning("JUnit 3+4 tests collections is skipped")
|
||||
return@block emptyList()
|
||||
additionalProperties[PathManager.PROPERTY_LOG_PATH] = "$systemPath${File.separator}log${File.separator}junit3and4"
|
||||
val exitCode34: Int = if (runJUnit34) {
|
||||
block("run junit 3+4 tests${spanNameSuffix}") {
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + additionalProperties + additionalPropertiesJUnit34,
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = options.bootstrapSuite,
|
||||
methodName = null,
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
}
|
||||
|
||||
val testClassesListFile = Files.createTempFile("tests-to-run-", ".list").apply { Files.delete(this) }
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + ("intellij.build.test.list.classes" to testClassesListFile.absolutePathString()),
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = options.bootstrapSuite,
|
||||
methodName = null,
|
||||
devBuildSettings = null,
|
||||
)
|
||||
return@block testClassesListFile.let { if (Files.exists(it)) it.readLines() else emptyList() }
|
||||
}
|
||||
else {
|
||||
0
|
||||
}
|
||||
if (runJUnit34) lastExitCode34 = exitCode34
|
||||
|
||||
if (testClassesJUnit5.isEmpty() && testClassesJUnit34.isEmpty() &&
|
||||
if (exitCode5 == NO_TESTS_ERROR && exitCode34 == NO_TESTS_ERROR &&
|
||||
// only check on the first (full) attempt
|
||||
attempt == 1 &&
|
||||
// a bucket might be empty for run configurations with too few tests due to imperfect tests balancing
|
||||
options.bucketsCount < 2) {
|
||||
throw NoTestsFound()
|
||||
}
|
||||
if (exitCode5 != 0 && exitCode5 != 1 && exitCode5 != NO_TESTS_ERROR) {
|
||||
throw RuntimeException("Unexpected exit code $exitCode5 when running JUnit 5 tests")
|
||||
}
|
||||
if (exitCode34 != 0 && exitCode34 != 1 && exitCode34 != NO_TESTS_ERROR) {
|
||||
throw RuntimeException("Unexpected exit code $exitCode34 when running JUnit 3+4 tests")
|
||||
}
|
||||
|
||||
if (options.isDedicatedTestRuntime == "class") {
|
||||
var hasFailures = false
|
||||
|
||||
suspend fun runOneClass(testClassName: String) {
|
||||
val exitCode = block("running test class '$testClassName'") {
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + ("idea.performance.tests.discovery.filter" to "true"),
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = testClassName,
|
||||
methodName = null,
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
}
|
||||
if (exitCode == 1) hasFailures = true // reported as test failure or assertNoUnhandledExceptions if exception
|
||||
else if (exitCode == NO_TESTS_ERROR) throw NoTestsFound()
|
||||
else if (exitCode != 0) throw RuntimeException("Unexpected exit code $exitCode when running tests in dedicated runtime (class mode)")
|
||||
if (runJUnit5) {
|
||||
val failedClassesJUnit5 = failedClassesJUnit5List.let { if (Files.exists(it)) it.readLines() else emptyList() }
|
||||
if (failedClassesJUnit5.isNotEmpty()) {
|
||||
messages.warning("Will rerun JUnit 5 tests: $failedClassesJUnit5")
|
||||
}
|
||||
|
||||
if (testClassesJUnit5.isNotEmpty()) {
|
||||
messages.info("Will run JUnit 5 tests:\n${testClassesJUnit5.joinToString("\n")}")
|
||||
for (s in testClassesJUnit5) {
|
||||
runOneClass(s)
|
||||
}
|
||||
}
|
||||
if (testClassesJUnit34.isNotEmpty()) {
|
||||
messages.info("Will run JUnit 3+4 tests:\n${testClassesJUnit34.joinToString("\n")}")
|
||||
for (s in testClassesJUnit34) {
|
||||
runOneClass(s)
|
||||
}
|
||||
}
|
||||
|
||||
// On TeamCity test failures themselves control the build status, no need to report them as additional errors
|
||||
if (hasFailures && !TeamCityHelper.isUnderTeamCity) {
|
||||
throw RuntimeException("Tests failed in dedicated runtime (class mode)")
|
||||
else {
|
||||
runJUnit5 = false
|
||||
}
|
||||
}
|
||||
else if (options.isDedicatedTestRuntime == "package") {
|
||||
var hasFailures = false
|
||||
|
||||
fun groupByPackages(tests: List<String>): Map<String, List<String>> {
|
||||
return tests.groupBy {
|
||||
val i = it.lastIndexOf('.')
|
||||
if (i != -1) it.substring(0, i) else ""
|
||||
}
|
||||
if (runJUnit34) {
|
||||
val failedClassesJUnit34 = failedClassesJUnit34List.let { if (Files.exists(it)) it.readLines() else emptyList() }
|
||||
if (failedClassesJUnit34.isNotEmpty()) {
|
||||
messages.warning("Will rerun JUnit 3+4 tests: $failedClassesJUnit34")
|
||||
}
|
||||
|
||||
suspend fun runOnePackage(entry: Map.Entry<String, List<String>>) {
|
||||
val packageName = entry.key
|
||||
val classes = entry.value
|
||||
|
||||
val exitCode = block("running tests in package '$packageName'") {
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + ("idea.performance.tests.discovery.filter" to "true"),
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = "__classes__",
|
||||
methodName = classes.joinToString(";"),
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
}
|
||||
if (exitCode == 1) hasFailures = true // reported as test failure or assertNoUnhandledExceptions if exception
|
||||
else if (exitCode == NO_TESTS_ERROR) throw NoTestsFound()
|
||||
else if (exitCode != 0) throw RuntimeException("Unexpected exit code $exitCode when running tests in dedicated runtime (package mode)")
|
||||
}
|
||||
|
||||
if (testClassesJUnit5.isNotEmpty()) {
|
||||
val packages = groupByPackages(testClassesJUnit5)
|
||||
messages.info(packages.entries.joinToString(prefix = "Will run JUnit 5 packages:\n", separator = "\n") { e ->
|
||||
e.value.joinToString(prefix = "${e.key}\n ", separator = "\n ")
|
||||
})
|
||||
for (entry in packages) {
|
||||
runOnePackage(entry)
|
||||
}
|
||||
}
|
||||
if (testClassesJUnit34.isNotEmpty()) {
|
||||
val packages = groupByPackages(testClassesJUnit34)
|
||||
messages.info(packages.entries.joinToString(prefix = "Will run JUnit 3+4 packages:\n", separator = "\n") { e ->
|
||||
e.value.joinToString(prefix = "${e.key}\n ", separator = "\n ")
|
||||
})
|
||||
for (entry in packages) {
|
||||
runOnePackage(entry)
|
||||
}
|
||||
}
|
||||
|
||||
// On TeamCity test failures themselves control the build status, no need to report them as additional errors
|
||||
if (hasFailures && !TeamCityHelper.isUnderTeamCity) {
|
||||
throw RuntimeException("Tests failed in dedicated runtime (package mode)")
|
||||
else {
|
||||
runJUnit34 = false
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
val failedClassesJUnit5List = Files.createTempFile("failed-classes-junit5-", ".list").apply { Files.delete(this) }
|
||||
val failedClassesJUnit34List = Files.createTempFile("failed-classes-junit34-", ".list").apply { Files.delete(this) }
|
||||
val additionalPropertiesJUnit5: Map<String, String> = failedClassesJUnit5List.let {
|
||||
if (options.attemptCount > 1) mapOf("intellij.build.test.retries.failedClasses.file" to "$it", "intellij.build.test.list.file" to "$it")
|
||||
else emptyMap()
|
||||
|
||||
// Check if tests failed after all retry attempts are exhausted
|
||||
val hadTestFailures = lastExitCode5 == 1 || lastExitCode34 == 1
|
||||
// On TeamCity test failures themselves control the build status, no need to report them as additional errors
|
||||
if (!TeamCityHelper.isUnderTeamCity) {
|
||||
if (hadTestFailures) {
|
||||
throw RuntimeException("Tests failed (JUnit5 exit code: $lastExitCode5, JUnit3+4 exit code: $lastExitCode34, $NO_TESTS_ERROR means no test for this test framework)")
|
||||
}
|
||||
val additionalPropertiesJUnit34: Map<String, String> = failedClassesJUnit34List.let {
|
||||
if (options.attemptCount > 1) mapOf("intellij.build.test.retries.failedClasses.file" to "$it", "intellij.build.test.list.file" to "$it")
|
||||
else emptyMap()
|
||||
}
|
||||
var runJUnit5 = !options.shouldSkipJUnit5Tests
|
||||
var runJUnit34 = !options.shouldSkipJUnit34Tests
|
||||
var lastExitCode5 = 0
|
||||
var lastExitCode34 = 0
|
||||
for (attempt in 1..options.attemptCount) {
|
||||
if (!runJUnit5 && !runJUnit34) break
|
||||
val spanNameSuffix = if (options.attemptCount > 1) " (attempt $attempt)" else ""
|
||||
val systemPath = systemProperties[PathManager.PROPERTY_SYSTEM_PATH]
|
||||
|
||||
val additionalProperties = mutableMapOf(PathManager.PROPERTY_LOG_PATH to "$systemPath${File.separator}log${File.separator}junit5")
|
||||
if (attempt > 1) {
|
||||
additionalProperties["intellij.build.test.ignoreFirstAndLastTests"] = "true"
|
||||
}
|
||||
|
||||
val exitCode5: Int = if (runJUnit5) {
|
||||
block("run junit 5 tests${spanNameSuffix}") {
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + additionalProperties + additionalPropertiesJUnit5,
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = null,
|
||||
methodName = null,
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
}
|
||||
}
|
||||
else {
|
||||
0
|
||||
}
|
||||
if (runJUnit5) lastExitCode5 = exitCode5
|
||||
|
||||
additionalProperties[PathManager.PROPERTY_LOG_PATH] = "$systemPath${File.separator}log${File.separator}junit3and4"
|
||||
val exitCode34: Int = if (runJUnit34) {
|
||||
block("run junit 3+4 tests${spanNameSuffix}") {
|
||||
runJUnit5Engine(
|
||||
mainModule = mainModule,
|
||||
systemProperties = systemProperties + additionalProperties + additionalPropertiesJUnit34,
|
||||
jvmArgs = jvmArgs,
|
||||
envVariables = envVariables,
|
||||
bootstrapClasspath = bootstrapClasspath,
|
||||
modulePath = modulePath,
|
||||
testClasspath = testClasspath,
|
||||
suiteName = options.bootstrapSuite,
|
||||
methodName = null,
|
||||
devBuildSettings = devBuildServerSettings,
|
||||
)
|
||||
}
|
||||
}
|
||||
else {
|
||||
0
|
||||
}
|
||||
if (runJUnit34) lastExitCode34 = exitCode34
|
||||
|
||||
if (exitCode5 == NO_TESTS_ERROR && exitCode34 == NO_TESTS_ERROR &&
|
||||
// only check on the first (full) attempt
|
||||
attempt == 1 &&
|
||||
// a bucket might be empty for run configurations with too few tests due to imperfect tests balancing
|
||||
options.bucketsCount < 2) {
|
||||
throw NoTestsFound()
|
||||
}
|
||||
if (exitCode5 != 0 && exitCode5 != 1 && exitCode5 != NO_TESTS_ERROR) {
|
||||
throw RuntimeException("Unexpected exit code $exitCode5 when running JUnit 5 tests")
|
||||
}
|
||||
if (exitCode34 != 0 && exitCode34 != 1 && exitCode34 != NO_TESTS_ERROR) {
|
||||
throw RuntimeException("Unexpected exit code $exitCode34 when running JUnit 3+4 tests")
|
||||
}
|
||||
|
||||
if (runJUnit5) {
|
||||
val failedClassesJUnit5 = failedClassesJUnit5List.let { if (Files.exists(it)) it.readLines() else emptyList() }
|
||||
if (failedClassesJUnit5.isNotEmpty()) {
|
||||
messages.warning("Will rerun JUnit 5 tests: $failedClassesJUnit5")
|
||||
}
|
||||
else {
|
||||
runJUnit5 = false
|
||||
}
|
||||
}
|
||||
|
||||
if (runJUnit34) {
|
||||
val failedClassesJUnit34 = failedClassesJUnit34List.let { if (Files.exists(it)) it.readLines() else emptyList() }
|
||||
if (failedClassesJUnit34.isNotEmpty()) {
|
||||
messages.warning("Will rerun JUnit 3+4 tests: $failedClassesJUnit34")
|
||||
}
|
||||
else {
|
||||
runJUnit34 = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if tests failed after all retry attempts are exhausted
|
||||
val hadTestFailures = lastExitCode5 == 1 || lastExitCode34 == 1
|
||||
// On TeamCity test failures themselves control the build status, no need to report them as additional errors
|
||||
if (!TeamCityHelper.isUnderTeamCity) {
|
||||
if (hadTestFailures) {
|
||||
throw RuntimeException("Tests failed (JUnit5 exit code: $lastExitCode5, JUnit3+4 exit code: $lastExitCode34, $NO_TESTS_ERROR means no test for this test framework)")
|
||||
}
|
||||
else {
|
||||
println("*** All tests passed ***")
|
||||
}
|
||||
else {
|
||||
println("*** All tests passed ***")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1303,7 +1138,7 @@ internal class TestingTasksImpl(context: CompilationContext, private val options
|
||||
appendJUnitStarter(classpath, context)
|
||||
}
|
||||
|
||||
if (!isBootstrapSuiteDefault || isRunningInBatchMode || options.isDedicatedTestRuntime != "false" || suiteName == null) {
|
||||
if (!isBootstrapSuiteDefault || options.isDedicatedTestRuntime != "false" || suiteName == null) {
|
||||
classpath.addAll(testClasspath)
|
||||
}
|
||||
|
||||
@@ -1401,14 +1236,6 @@ internal class TestingTasksImpl(context: CompilationContext, private val options
|
||||
|
||||
private val isBootstrapSuiteDefault: Boolean
|
||||
get() = options.bootstrapSuite == TestingOptions.BOOTSTRAP_SUITE_DEFAULT
|
||||
|
||||
private val isRunningInBatchMode: Boolean
|
||||
get() {
|
||||
return options.batchTestIncludes != null &&
|
||||
options.testPatterns == null &&
|
||||
options.testConfigurations == null &&
|
||||
options.testGroups == TestingOptions.ALL_EXCLUDE_DEFINED_GROUP
|
||||
}
|
||||
}
|
||||
|
||||
private fun appendJUnitStarter(classPath: MutableList<String>, context: CompilationContext) {
|
||||
|
||||
Reference in New Issue
Block a user