-
Notifications
You must be signed in to change notification settings - Fork 12
Add testProcess{Config} task to accept user options #581
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
42d4d54
6f5c7ba
a13c32a
0a80e41
710ac93
9b9d36e
dff76a3
9e9d6a0
1d5a380
15dbbf7
799203b
50547f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -399,20 +399,16 @@ class ProfilerTestPlugin : Plugin<Project> { | |
| } | ||
|
|
||
| // Environment variables | ||
| testConfig.environmentVariables.forEach { (key, value) -> | ||
| execTask.environment(key, value) | ||
| } | ||
|
|
||
| // CRITICAL FIX: Remove LD_LIBRARY_PATH to let RPATH work correctly | ||
| // CRITICAL FIX: Filter out LD_LIBRARY_PATH to let RPATH work correctly | ||
| // The test JDK's launcher has RPATH set to find its own libraries ($ORIGIN/../lib/jli) | ||
| // But LD_LIBRARY_PATH overrides RPATH and causes it to load the wrong libjli.so | ||
| // Solution: Unset LD_LIBRARY_PATH entirely to let RPATH take precedence | ||
| execTask.doFirst { | ||
| val currentLdLibPath = (execTask.environment["LD_LIBRARY_PATH"] as? String) ?: System.getenv("LD_LIBRARY_PATH") | ||
| if (!currentLdLibPath.isNullOrEmpty()) { | ||
| project.logger.info("Removing LD_LIBRARY_PATH to prevent cross-JDK library conflicts (was: $currentLdLibPath)") | ||
| execTask.environment.remove("LD_LIBRARY_PATH") | ||
| // Solution: Never propagate LD_LIBRARY_PATH so RPATH takes precedence | ||
| testConfig.environmentVariables.forEach { (key, value) -> | ||
| if (key == "LD_LIBRARY_PATH") { | ||
| project.logger.info("Removing LD_LIBRARY_PATH to prevent cross-JDK library conflicts (was: $value)") | ||
| return@forEach | ||
| } | ||
| execTask.environment(key, value) | ||
| } | ||
|
|
||
| // Sanitizer conditions | ||
|
|
@@ -429,6 +425,116 @@ class ProfilerTestPlugin : Plugin<Project> { | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Create Exec-based test task that always runs in a separate process. | ||
| * Available on all platforms. Supports -Pprofiler.options for overriding profiler settings. | ||
| * Task name: testProcess<Config> (e.g., testProcessDebug, testProcessRelease) | ||
| */ | ||
| private fun createProcessTestTask( | ||
| project: Project, | ||
| extension: ProfilerTestExtension, | ||
| testConfig: TestTaskConfiguration, | ||
| testCfg: Configuration, | ||
| sourceSets: SourceSetContainer | ||
| ) { | ||
| val taskName = "testProcess${testConfig.configName.replaceFirstChar { it.uppercase() }}" | ||
| project.tasks.register(taskName, Exec::class.java) { | ||
| val execTask = this | ||
| execTask.description = "Runs tests in separate process with ${testConfig.configName} library (supports -Pprofiler.options)" | ||
| execTask.group = "verification" | ||
| execTask.onlyIf { testConfig.isActive && !project.hasProperty("skip-tests") } | ||
|
|
||
| // Dependencies | ||
| execTask.dependsOn(project.tasks.named("compileTestJava")) | ||
| execTask.dependsOn(testCfg) | ||
| execTask.dependsOn(sourceSets.getByName("test").output) | ||
|
|
||
| // Configure at execution time to capture properties | ||
| execTask.doFirst { | ||
| execTask.executable = PlatformUtils.testJavaExecutable() | ||
|
|
||
| val allArgs = mutableListOf<String>() | ||
|
|
||
| // JVM args | ||
| allArgs.addAll(testConfig.standardJvmArgs) | ||
| // Version-gated at execution time, when the real test JVM (JAVA_TEST_HOME) is resolvable. | ||
| allArgs.addAll(carrierExportJvmArgs(project)) | ||
| if (extension.nativeLibDir.isPresent) { | ||
| allArgs.add("-Djava.library.path=${extension.nativeLibDir.get().asFile.absolutePath}") | ||
| } | ||
| allArgs.addAll(testConfig.extraJvmArgs) | ||
|
zhengyu123 marked this conversation as resolved.
|
||
| allArgs.addAll(testConfig.configJvmArgs) | ||
|
|
||
| // System properties | ||
| testConfig.systemProperties.forEach { (key, value) -> | ||
| allArgs.add("-D$key=$value") | ||
| } | ||
|
|
||
| // Test filter from -Ptests property | ||
| val testsFilter = project.findProperty("tests") as String? | ||
| if (testsFilter != null) { | ||
| allArgs.add("-Dtest.filter=$testsFilter") | ||
| } | ||
|
|
||
| // Profiler options from -Pprofiler.options property | ||
| val profilerOptions = project.findProperty("profiler.options") as String? | ||
| if (profilerOptions != null) { | ||
| allArgs.add("-Dddprof.test.options=$profilerOptions") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This only adds Useful? React with 👍 / 👎. |
||
| } | ||
|
|
||
| // Classpath | ||
| allArgs.add("-cp") | ||
| allArgs.add(testConfig.testClasspath.asPath) | ||
|
|
||
| // Use custom test runner | ||
| allArgs.add("com.datadoghq.profiler.test.ProfilerTestRunner") | ||
|
|
||
| execTask.args = allArgs | ||
| } | ||
|
|
||
| // Environment variables | ||
| testConfig.environmentVariables.forEach { (key, value) -> | ||
| execTask.environment(key, value) | ||
| } | ||
|
|
||
| // Remove LD_LIBRARY_PATH to let RPATH work correctly | ||
| execTask.doFirst { | ||
|
zhengyu123 marked this conversation as resolved.
|
||
| val currentLdLibPath = (execTask.environment["LD_LIBRARY_PATH"] as? String) ?: System.getenv("LD_LIBRARY_PATH") | ||
| if (!currentLdLibPath.isNullOrEmpty()) { | ||
| project.logger.info("Removing LD_LIBRARY_PATH to prevent cross-JDK library conflicts (was: $currentLdLibPath)") | ||
| execTask.environment.remove("LD_LIBRARY_PATH") | ||
| } | ||
| } | ||
|
|
||
| // Sanitizer conditions | ||
| when (testConfig.configName) { | ||
| "asan" -> { | ||
| execTask.onlyIf { | ||
| PlatformUtils.locateLibasan() != null && | ||
| !PlatformUtils.isTestJvmJ9() | ||
| } | ||
| // Unlike the Test task's asan variant (setForkEvery(25) above), this task | ||
| // is a single un-forked Exec process running the whole suite in one JVM — | ||
| // there is no forkEvery equivalent for Exec. An unfiltered run would | ||
| // reintroduce the late-suite OOM under ASan's -Xmx512m cap that forkEvery | ||
| // was added to avoid, so require -Ptests to bound the run instead. | ||
| execTask.doFirst { | ||
| if (project.findProperty("tests") == null) { | ||
| throw GradleException( | ||
| "$taskName requires -Ptests=<ClassName|ClassName.method> for the " + | ||
| "asan config: it runs in a single process with no forkEvery " + | ||
| "equivalent, and JFR/JMC allocations accumulate under ASan's " + | ||
| "-Xmx512m heap cap until an unfiltered run OOMs late in the suite. " + | ||
| "For full-suite ASan coverage, use the forked testAsan task instead." | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| "tsan" -> execTask.onlyIf { false } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private fun generateMultiConfigTasks(project: Project, extension: ProfilerTestExtension) { | ||
| val nativeBuildExt = project.rootProject.extensions.findByType(NativeBuildExtension::class.java) | ||
| ?: return // No native build extension, nothing to generate | ||
|
|
@@ -496,6 +602,10 @@ class ProfilerTestPlugin : Plugin<Project> { | |
| createTestTask(project, extension, testConfig, testCfg, sourceSets) | ||
| } | ||
|
|
||
| // Create process-based test task (always uses Exec, available on all platforms) | ||
| // Supports -Pprofiler.options for overriding profiler settings | ||
| createProcessTestTask(project, extension, testConfig, testCfg, sourceSets) | ||
|
|
||
| // Create application tasks for specified configs | ||
| if (configName in applicationConfigs && appMainClass.isNotEmpty()) { | ||
| // Create main configuration | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the normal musl Exec test path runs with
LD_LIBRARY_PATHalready set in the CI shell, this loop only filters values coming fromtestConfig.environmentVariables; Gradle Exec tasks inherit the current process environment by default, so the ambientLD_LIBRARY_PATHremains inexecTask.environment. The previousdoFirstremoved the inherited value as well, and without that removal$JAVA_TEST_HOME/bin/javacan again load the wronglibjli.so, which is the cross-JDK failure this block is meant to prevent.Useful? React with 👍 / 👎.