Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ repository on GitHub.
[[v6.2.0-M1-junit-platform-bug-fixes]]
==== Bug Fixes

*
* The Suite Engine no longer logs "No TestIdentifier with unique ID" for failing tests.

[[v6.2.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 @@ -23,12 +23,11 @@
import org.junit.platform.engine.support.store.Namespace;
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestExecutionListener;
import org.junit.platform.launcher.core.EngineDiscoveryOrchestrator;
import org.junit.platform.launcher.core.EngineExecutionOrchestrator;
import org.junit.platform.launcher.core.LauncherDiscoveryResult;
import org.junit.platform.launcher.core.ServiceLoaderTestEngineRegistry;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.launcher.listeners.TestExecutionSummary;

/**
* @since 1.8
Expand Down Expand Up @@ -59,12 +58,12 @@ LauncherDiscoveryResult discover(LauncherDiscoveryRequest discoveryRequest, Uniq
return discoveryOrchestrator.discover(discoveryRequest, parentId);
}

TestExecutionSummary execute(LauncherDiscoveryResult discoveryResult, EngineExecutionListener executionListener,
NamespacedHierarchicalStore<Namespace> requestLevelStore, CancellationToken cancellationToken) {
SummaryGeneratingListener listener = new SummaryGeneratingListener();
executionOrchestrator.execute(discoveryResult, executionListener, listener, requestLevelStore,
void execute(LauncherDiscoveryResult discoveryResult, EngineExecutionListener executionListener,
TestExecutionListener testExecutionListener, NamespacedHierarchicalStore<Namespace> requestLevelStore,
CancellationToken cancellationToken) {

executionOrchestrator.execute(discoveryResult, executionListener, testExecutionListener, requestLevelStore,
cancellationToken);
return listener.getSummary();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import java.lang.reflect.Method;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.BiFunction;
import java.util.function.Predicate;

Expand Down Expand Up @@ -47,8 +48,11 @@
import org.junit.platform.engine.support.store.NamespacedHierarchicalStore;
import org.junit.platform.launcher.LauncherDiscoveryListener;
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.LauncherDiscoveryResult;
import org.junit.platform.launcher.listeners.TestExecutionSummary;
import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.suite.api.Suite;
import org.junit.platform.suite.api.SuiteDisplayName;

Expand Down Expand Up @@ -170,8 +174,8 @@ void execute(EngineExecutionListener executionListener, NamespacedHierarchicalSt

executeBeforeSuiteMethods(throwableCollector);

TestExecutionSummary summary = executeTests(executionListener, requestLevelStore, cancellationToken,
throwableCollector);
var summary = new SuiteSummaryGeneratingListener();
executeTests(executionListener, summary, requestLevelStore, cancellationToken, throwableCollector);

executeAfterSuiteMethods(throwableCollector);

Expand All @@ -191,12 +195,12 @@ private void executeBeforeSuiteMethods(ThrowableCollector throwableCollector) {
}
}

private @Nullable TestExecutionSummary executeTests(EngineExecutionListener executionListener,
private void executeTests(EngineExecutionListener executionListener, TestExecutionListener testExecutionListener,
NamespacedHierarchicalStore<Namespace> requestLevelStore, CancellationToken cancellationToken,
ThrowableCollector throwableCollector) {

if (throwableCollector.isNotEmpty()) {
return null;
return;
}

// #2838: The discovery result from a suite may have been filtered by
Expand All @@ -205,7 +209,7 @@ private void executeBeforeSuiteMethods(ThrowableCollector throwableCollector) {
LauncherDiscoveryResult discoveryResult = requireNonNull(this.launcherDiscoveryResult).withRetainedEngines(
getChildren()::contains);

return requireNonNull(launcher).execute(discoveryResult, executionListener, requestLevelStore,
requireNonNull(launcher).execute(discoveryResult, executionListener, testExecutionListener, requestLevelStore,
cancellationToken);
}

Expand All @@ -215,13 +219,13 @@ private void executeAfterSuiteMethods(ThrowableCollector throwableCollector) {
}
}

private TestExecutionResult computeTestExecutionResult(@Nullable TestExecutionSummary summary,
private TestExecutionResult computeTestExecutionResult(SuiteSummaryGeneratingListener summary,
ThrowableCollector throwableCollector) {
var throwable = throwableCollector.getThrowable();
if (throwable != null) {
return TestExecutionResult.failed(throwable);
}
if (failIfNoTests && requireNonNull(summary).getTestsFoundCount() == 0) {
if (failIfNoTests && summary.getTestsFoundCount() == 0) {
return TestExecutionResult.failed(new NoTestsDiscoveredException(suiteClass));
}
return TestExecutionResult.successful();
Expand Down Expand Up @@ -280,4 +284,25 @@ public void issueEncountered(UniqueId engineUniqueId, DiscoveryIssue issue) {
this.discoveryListener.issueEncountered(engineUniqueId, transformedIssue);
}
}

/**
* A minimal implementation of the {@link SummaryGeneratingListener}.
* <p>
* The {@code SummaryGeneratingListener} assumes that all the ancestors all
* test descriptors are in the test plan. This isn't true for the suite engine
* which only executes a subtree of the test plan. This implementation tracks
* only the essentials.
*/
private static final class SuiteSummaryGeneratingListener implements TestExecutionListener {
private final AtomicLong testsFound = new AtomicLong();

@Override
public void testPlanExecutionStarted(TestPlan testPlan) {
this.testsFound.set(testPlan.countTestIdentifiers(TestIdentifier::isTest));
}

long getTestsFoundCount() {
return testsFound.get();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,18 @@
import static org.mockito.Mockito.when;

import java.nio.file.Path;
import java.util.logging.Level;

import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.fixtures.TrackLogRecords;
import org.junit.jupiter.api.io.TempDir;
import org.junit.jupiter.engine.descriptor.ClassTestDescriptor;
import org.junit.jupiter.engine.descriptor.JupiterEngineDescriptor;
import org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.platform.commons.logging.LogRecordListener;
import org.junit.platform.engine.CancellationToken;
import org.junit.platform.engine.DiscoveryIssue;
import org.junit.platform.engine.DiscoveryIssue.Severity;
Expand All @@ -67,6 +70,7 @@
import org.junit.platform.suite.engine.testcases.ErroneousTestCase;
import org.junit.platform.suite.engine.testcases.JUnit4TestsTestCase;
import org.junit.platform.suite.engine.testcases.MultipleTestsTestCase;
import org.junit.platform.suite.engine.testcases.SingleFailingTestTestCase;
import org.junit.platform.suite.engine.testcases.SingleTestTestCase;
import org.junit.platform.suite.engine.testcases.TaggedTestTestCase;
import org.junit.platform.suite.engine.testsuites.AbstractSuite;
Expand All @@ -80,6 +84,7 @@
import org.junit.platform.suite.engine.testsuites.EmptyTestCaseSuite;
import org.junit.platform.suite.engine.testsuites.EmptyTestCaseWithFailIfNoTestFalseSuite;
import org.junit.platform.suite.engine.testsuites.ErroneousTestSuite;
import org.junit.platform.suite.engine.testsuites.FailingSuite;
import org.junit.platform.suite.engine.testsuites.InheritedSuite;
import org.junit.platform.suite.engine.testsuites.MultiEngineSuite;
import org.junit.platform.suite.engine.testsuites.MultipleSuite;
Expand Down Expand Up @@ -630,6 +635,28 @@ void threePartCyclicSuite() {
// @formatter:on
}

@Test
void failingSuite(@TrackLogRecords LogRecordListener listener) {
// @formatter:off
EngineTestKit.Builder testKit = EngineTestKit.engine(ENGINE_ID)
.selectors(selectClass(FailingSuite.class));

assertThat(testKit.discover().getDiscoveryIssues())
.isEmpty();

testKit
.execute()
.testEvents()
.debug()
.assertThatEvents()
.haveExactly(1, event(test(FailingSuite.class.getName()), finishedWithFailure()))
.haveExactly(1, event(test(SingleFailingTestTestCase.class.getName()),finishedWithFailure()));
// @formatter:on

// Warnings from failing listeners.
assertThat(listener.stream(Level.WARNING)).isEmpty();
}

@Test
void selectByIdentifier() {
// @formatter:off
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright 2015-2026 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 org.junit.platform.suite.engine.testcases;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

/**
* @since 1.8
*/
public class SingleFailingTestTestCase {

@Test
void test() {
Assertions.fail();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2015-2026 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 org.junit.platform.suite.engine.testsuites;

import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
import org.junit.platform.suite.engine.testcases.SingleFailingTestTestCase;

/**
* @since 1.8
*/
@Suite
@SelectClasses(SingleFailingTestTestCase.class)
public class FailingSuite {
}
Loading