Skip to content

Commit

Permalink
Fix class-level execution conditions on GraalVM (#3785)
Browse files Browse the repository at this point in the history
* Update to latest version of native build tools
* Track descendants of skipped containers
* Disable integration test if GRAALVM_HOME env var is not set

Fixes #3745.
  • Loading branch information
marcphilipp committed Apr 18, 2024
1 parent 3e03c97 commit 8b24ca8
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ repository on GitHub.
inside Spring Boot executable jars for Spring Boot 3.2 and later.
* The `junit-platform-suite-engine` now includes configuration provided via
`@ConfigurationParameter` when selecting tests by `UniqueId`.
* In order to support using `@EnabledInNativeImage` on test classes,
`UniqueIdTrackingListener` now tracks descendants of skipped test containers.

[[release-notes-5.11.0-M1-junit-platform-deprecations-and-breaking-changes]]
==== Deprecations and Breaking Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ public class UniqueIdTrackingListener implements TestExecutionListener {
private final List<String> uniqueIds = new ArrayList<>();

private boolean enabled;
private TestPlan testPlan;

public UniqueIdTrackingListener() {
// to avoid missing-explicit-ctor warning
Expand All @@ -138,22 +139,38 @@ public UniqueIdTrackingListener() {
@Override
public void testPlanExecutionStarted(TestPlan testPlan) {
this.enabled = testPlan.getConfigurationParameters().getBoolean(LISTENER_ENABLED_PROPERTY_NAME).orElse(false);
this.testPlan = testPlan;
}

@Override
public void executionSkipped(TestIdentifier testIdentifier, String reason) {
trackTestUid(testIdentifier);
if (this.enabled) {
// When a container is skipped, there are no events for its children.
// Therefore, in order to track them, we need to traverse the subtree.
trackTestUidRecursively(testIdentifier);
}
}

@Override
public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) {
trackTestUid(testIdentifier);
if (this.enabled) {
trackTestUid(testIdentifier);
}
}

private void trackTestUidRecursively(TestIdentifier testIdentifier) {
boolean tracked = trackTestUid(testIdentifier);
if (!tracked) {
this.testPlan.getChildren(testIdentifier).forEach(this::trackTestUidRecursively);
}
}

private void trackTestUid(TestIdentifier testIdentifier) {
if (this.enabled && testIdentifier.isTest()) {
private boolean trackTestUid(TestIdentifier testIdentifier) {
if (testIdentifier.isTest()) {
this.uniqueIds.add(testIdentifier.getUniqueId());
return true;
}
return false;
}

@Override
Expand All @@ -178,6 +195,7 @@ public void testPlanExecutionFinished(TestPlan testPlan) {
logger.error(ex, () -> "Failed to write unique IDs to output file " + outputFile.toAbsolutePath());
}
}
this.testPlan = null;
}

private Path createOutputFile(ConfigurationParameters configurationParameters) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.assertj.core.api.Condition;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.junit.platform.engine.TestDescriptor;
Expand All @@ -54,6 +55,7 @@
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.TestIdentifier;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherFactory;
import org.junit.platform.testkit.engine.EngineTestKit;
import org.junit.platform.testkit.engine.Event;
Expand All @@ -79,9 +81,10 @@ class UniqueIdTrackingListenerIntegrationTests {
private static final String testD = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase3]/[method:testD()]";
private static final String testE = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase4]/[method:testE()]";
private static final String testF = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$TestCase4]/[method:testF()]";
private static final String testG = "[engine:junit-jupiter]/[class:org.junit.platform.launcher.listeners.UniqueIdTrackingListenerIntegrationTests$DisabledTestCase]/[nested-class:Inner]/[method:testG()]";

private static final String[] expectedUniqueIds = { passingTest, skippedTest, abortedTest, failingTest,
dynamicTest1, dynamicTest2, testA, testB };
dynamicTest1, dynamicTest2, testA, testB, testG };

private static final String[] expectedConcurrentUniqueIds = { testA, testB, testC, testD, testE, testF };

Expand Down Expand Up @@ -225,11 +228,21 @@ private static List<String> executeTests(Map<String, String> configurationParame
.build();
LauncherFactory.create().execute(request, new TestExecutionListener() {

private TestPlan testPlan;

@Override
public void testPlanExecutionStarted(TestPlan testPlan) {
this.testPlan = testPlan;
}

@Override
public void executionSkipped(TestIdentifier testIdentifier, String reason) {
if (testIdentifier.isTest()) {
uniqueIds.add(testIdentifier.getUniqueId());
}
else {
this.testPlan.getChildren(testIdentifier).forEach(child -> executionSkipped(child, reason));
}
}

@Override
Expand All @@ -243,7 +256,8 @@ public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult
}

private static ClassSelector[] selectClasses() {
return new ClassSelector[] { selectClass(TestCase1.class), selectClass(TestCase2.class) };
return new ClassSelector[] { selectClass(TestCase1.class), selectClass(TestCase2.class),
selectClass(DisabledTestCase.class) };
}

private static Stream<Path> findFiles(String dir, String prefix) throws IOException {
Expand Down Expand Up @@ -340,4 +354,16 @@ void testF() {
}
}

@Disabled
static class DisabledTestCase {

@Nested
class Inner {

@Test
void testG() {
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ tasks.test {
graalvmNative {
binaries {
named("test") {
buildArgs.add("--initialize-at-build-time=org.junit.platform.launcher.core.LauncherConfig")
buildArgs.add("--initialize-at-build-time=org.junit.jupiter.engine.config.InstantiatingConfigurationParameterConverter")
buildArgs.add("-H:+ReportExceptionStackTraces")
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
pluginManagement {
plugins {
id("org.graalvm.buildtools.native") version "0.9.13"
id("org.graalvm.buildtools.native") version "0.10.1"
}
repositories {
mavenCentral()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2015-2024 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/

package com.example.project;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledInNativeImage;

@EnabledInNativeImage
class ClassLevelAnnotationTests {
@Nested
class Inner {
@Test
void test() {
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,17 @@

package platform.tooling.support.tests;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assumptions.assumeFalse;
import static platform.tooling.support.tests.XmlAssertions.verifyContainsExpectedStartedOpenTestReport;

import java.nio.file.Paths;
import java.time.Duration;

import de.sormuras.bartholdy.tool.GradleWrapper;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.junit.jupiter.api.extension.DisabledOnOpenJ9;

import platform.tooling.support.MavenRepo;
Expand All @@ -31,6 +30,7 @@
* @since 1.9.1
*/
@DisabledOnOpenJ9
@EnabledIfEnvironmentVariable(named = "GRAALVM_HOME", matches = ".+")
class GraalVmStarterTests {

@Test
Expand All @@ -40,23 +40,18 @@ void runsTestsInNativeImage() {
.setProject("graalvm-starter") //
.addArguments("-Dmaven.repo=" + MavenRepo.dir()) //
.addArguments("javaToolchains", "nativeTest", "--no-daemon", "--stacktrace") //
.addArguments("-Porg.gradle.java.installations.fromEnv=GRAALVM_HOME") //
.setTimeout(Duration.ofMinutes(10)) //
.build();

var result = request.run();

assertFalse(result.isTimedOut(), () -> "tool timed out: " + result);

assumeFalse(
result.getOutputLines("err").stream().anyMatch(
line -> line.contains("No locally installed toolchains match")),
"Abort test if GraalVM is not installed");

assertEquals(0, result.getExitCode());
assertTrue(result.getOutputLines("out").stream().anyMatch(line -> line.contains("BUILD SUCCESSFUL")));

var testResultsDir = Request.WORKSPACE.resolve(request.getWorkspace()).resolve("build/test-results/test");
verifyContainsExpectedStartedOpenTestReport(testResultsDir);
assertThat(result.getOutputLines("out")) //
.anyMatch(line -> line.contains("CalculatorTests > 1 + 1 = 2 SUCCESSFUL")) //
.anyMatch(line -> line.contains("CalculatorTests > 1 + 100 = 101 SUCCESSFUL")) //
.anyMatch(line -> line.contains("ClassLevelAnnotationTests$Inner > test() SUCCESSFUL")) //
.anyMatch(line -> line.contains("BUILD SUCCESSFUL"));
}
}

0 comments on commit 8b24ca8

Please sign in to comment.