Skip to content
Merged
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 @@ -24,7 +24,9 @@ guidance on upgrading from JUnit 5.x.y to 6.0.0.
[[release-notes-6.0.0-RC2-junit-platform-bug-fixes]]
==== Bug Fixes

* ❓
* The `Launcher` (specifically `LauncherDiscoveryResult`) now retains the original
`TestEngine` registration order after pruning test engines without tests, thereby
ensuring reliable test execution order of multiple test engines.

[[release-notes-6.0.0-RC2-junit-platform-deprecations-and-breaking-changes]]
==== Deprecations and Breaking Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import static java.util.Collections.unmodifiableMap;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Collectors.toMap;
import static org.apiguardian.api.API.Status.INTERNAL;

import java.util.Collection;
Expand Down Expand Up @@ -96,12 +95,9 @@ public LauncherDiscoveryResult withRetainedEngines(Predicate<? super TestDescrip
}

private Map<TestEngine, EngineResultInfo> retainEngines(Predicate<? super TestDescriptor> predicate) {
// @formatter:off
return this.testEngineResults.entrySet()
.stream()
.filter(entry -> predicate.test(entry.getValue().getRootDescriptor()))
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
// @formatter:on
var retainedEngines = new LinkedHashMap<>(this.testEngineResults);
retainedEngines.entrySet().removeIf(entry -> !predicate.test(entry.getValue().getRootDescriptor()));
return retainedEngines;
}

static class EngineResultInfo {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* Copyright 2015-2025 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.launcher.core;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.LinkedHashMap;
import java.util.Map;

import org.junit.jupiter.api.Test;
import org.junit.platform.engine.EngineDiscoveryRequest;
import org.junit.platform.engine.ExecutionRequest;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.engine.TestEngine;
import org.junit.platform.engine.UniqueId;
import org.junit.platform.launcher.core.LauncherDiscoveryResult.EngineResultInfo;

/**
* Unit tests for {@link LauncherDiscoveryResult}.
*/
class LauncherDiscoveryResultTests {

/**
* @see <a href="https://github.com/junit-team/junit-framework/issues/4862">GitHub issue #4862</>
*/
@Test
void withRetainedEnginesRetainsLinkedHashMapSemantics() {
TestEngine engine1 = new DummyEngine1();
TestEngine engine2 = new DummyEngine2();
TestEngine engine3 = new DummyEngine3();
TestEngine engine4 = new DummyEngine4();

TestDescriptor rootDescriptor1 = mock();
TestDescriptor rootDescriptor2 = mock();
TestDescriptor rootDescriptor3 = mock();
TestDescriptor rootDescriptor4 = mock();
when(rootDescriptor1.isTest()).thenReturn(true);
when(rootDescriptor2.isTest()).thenReturn(false);
when(rootDescriptor3.isTest()).thenReturn(false);
when(rootDescriptor4.isTest()).thenReturn(true);

EngineResultInfo engineResultInfo1 = mock();
EngineResultInfo engineResultInfo2 = mock();
EngineResultInfo engineResultInfo3 = mock();
EngineResultInfo engineResultInfo4 = mock();
when(engineResultInfo1.getRootDescriptor()).thenReturn(rootDescriptor1);
when(engineResultInfo2.getRootDescriptor()).thenReturn(rootDescriptor2);
when(engineResultInfo3.getRootDescriptor()).thenReturn(rootDescriptor3);
when(engineResultInfo4.getRootDescriptor()).thenReturn(rootDescriptor4);

@SuppressWarnings("serial")
Map<TestEngine, EngineResultInfo> engineResults = new LinkedHashMap<>() {
{
put(engine1, engineResultInfo1);
put(engine2, engineResultInfo2);
put(engine3, engineResultInfo3);
put(engine4, engineResultInfo4);
}
};

assertThat(engineResults.keySet()).containsExactly(engine1, engine2, engine3, engine4);

LauncherDiscoveryResult discoveryResult = new LauncherDiscoveryResult(engineResults, mock(), mock());
assertThat(discoveryResult.getTestEngines()).containsExactly(engine1, engine2, engine3, engine4);

LauncherDiscoveryResult withRetainedEngines = discoveryResult.withRetainedEngines(TestDescriptor::isTest);

assertThat(withRetainedEngines.getTestEngines()).containsExactly(engine1, engine4);
}

private static abstract class AbstractDummyEngine implements TestEngine {

@Override
public String getId() {
return getClass().getSimpleName();
}

@Override
public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {
throw new UnsupportedOperationException("discover");
}

@Override
public void execute(ExecutionRequest request) {
throw new UnsupportedOperationException("execute");
}
}

private static class DummyEngine1 extends AbstractDummyEngine {
}

private static class DummyEngine2 extends AbstractDummyEngine {
}

private static class DummyEngine3 extends AbstractDummyEngine {
}

private static class DummyEngine4 extends AbstractDummyEngine {
}

}
Loading