Skip to content

Commit

Permalink
Fix formatting of tests run by the vintage engine (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Aichler authored and maichler committed Jan 29, 2019
1 parent d5b5eb8 commit 51755b6
Show file tree
Hide file tree
Showing 14 changed files with 486 additions and 86 deletions.
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ lazy val library = (project in file("src/library"))
),
libraryDependencies ++= Seq(
"org.junit.jupiter" % "junit-jupiter-params" % Versions.junitJupiter % Test,
"org.junit.vintage" % "junit-vintage-engine" % Versions.junitVintage % Test,
"org.hamcrest" % "hamcrest-library" % "1.3" % Test,
"org.mockito" % "mockito-core" % "2.23.4" % Test,
"com.novocode" % "junit-interface" % "0.11" % Test,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import net.aichler.jupiter.internal.listeners.FlatPrintingTestListener;
import net.aichler.jupiter.internal.listeners.TreePrintingTestListener;
import net.aichler.jupiter.internal.options.Options;
import org.junit.jupiter.api.Test;
import org.junit.platform.engine.TestSource;
import org.junit.platform.engine.UniqueId;
import org.junit.platform.engine.UniqueId.Segment;
Expand All @@ -33,11 +34,15 @@
import sbt.testing.Logger;
import sbt.testing.TaskDef;

import java.io.FileWriter;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

Expand Down Expand Up @@ -156,9 +161,8 @@ public String decodeName(String name) {
*/
public String formatIdentifier(TestPlan testPlan, TestIdentifier identifier) {

return getPath(testPlan, identifier).stream().skip(1)
.map(this::toName)
.collect(Collectors.joining());
return new TestIdentifierFormatter(testPlan, identifier)
.format();
}

/**
Expand Down Expand Up @@ -295,74 +299,166 @@ private String buildColoredMethodName(String m, Color c1, Color c2) {
return b.toString();
}

private List<TestIdentifier> getPath(TestPlan testPlan, TestIdentifier identifier) {
/**
* Helper class which knows how to format a {@link TestIdentifier}.
*/
class TestIdentifierFormatter {

final static String VINTAGE_ENGINE = "junit-vintage";

final TestPlan testPlan;
final TestIdentifier identifier;

private String testEngine;

List<TestIdentifier> result = new ArrayList<>();
TestIdentifierFormatter(TestPlan testPlan, TestIdentifier testIdentifier) {

do {
result.add(identifier);
identifier = testPlan.getParent(identifier).orElse(null);
this.testPlan = testPlan;
this.identifier = testIdentifier;
}
while (null != identifier);

Collections.reverse(result);
return result;
}
/**
* @return The formatted test name using the configured color theme.
*/
public String format() {

private String toName(TestIdentifier identifier) {
final List<TestIdentifier> path = getPath(testPlan, identifier);

String name = identifier.getLegacyReportingName();
List<Segment> segments = UniqueId.parse(identifier.getUniqueId()).getSegments();
testEngine = UniqueId.parse(identifier.getUniqueId())
.getEngineId()
.orElse(null);

if (!segments.isEmpty()) {
Segment lastSegment = segments.get(segments.size() - 1);
name = toName(lastSegment);
return path.stream()
.skip(1)
.map(this::toName)
.filter(Objects::nonNull)
.collect(Collectors.joining());
}

return name;
}
private List<TestIdentifier> getPath(TestPlan testPlan, TestIdentifier identifier) {

private String toName(Segment segment) {

String name;

switch (segment.getType()) {
case "class":
String[] parts = segment.getValue().split("\\.");
name = IntStream.range(0, parts.length).mapToObj(i -> {
if (i == (parts.length - 1))
return colorTheme.container().format(parts[i]);
return parts[i];

}).collect(Collectors.joining("."));
break;
case "nested-class":
name = colorTheme.container().format("$" + segment.getValue());
break;
case "method":
name = colorTheme.testMethod().format("#" + segment.getValue());
break;
case "test-factory":
name = colorTheme.testFactory().format("#" + segment.getValue());
break;
case "dynamic-test":
name = colorTheme.dynamicTest().format(":" + segment.getValue());
break;
case "test-template":
name = colorTheme.testTemplate().format("#" + segment.getValue());
break;
case "test-template-invocation":
name = colorTheme.container().format(":" + segment.getValue());
break;
default:
name = segment.getValue();
break;
List<TestIdentifier> result = new ArrayList<>();

do {
result.add(identifier);
identifier = testPlan.getParent(identifier).orElse(null);
}
while (null != identifier);

Collections.reverse(result);
return result;
}

/**
* @return A formatted display name on success, {@code NULL} if the given
* identifier should be ignored.
*/
private String toName(TestIdentifier identifier) {

String name = identifier.getDisplayName();
List<Segment> segments = UniqueId.parse(identifier.getUniqueId()).getSegments();

if (!segments.isEmpty()) {
Segment lastSegment = segments.get(segments.size() - 1);

name = VINTAGE_ENGINE.equals(testEngine)
? toVintageName(identifier, lastSegment)
: toName(lastSegment);
}

return name;
}

/*
* Formats a test segment from junit-jupiter.
*/
private String toName(Segment segment) {

String name;

switch (segment.getType()) {
case "class":
name = colorClassName(segment.getValue(), colorTheme.container());
break;
case "nested-class":
name = colorTheme.container().format("$" + segment.getValue());
break;
case "method":
name = colorTheme.testMethod().format("#" + segment.getValue());
break;
case "test-factory":
name = colorTheme.testFactory().format("#" + segment.getValue());
break;
case "dynamic-test":
name = colorTheme.dynamicTest().format(":" + segment.getValue());
break;
case "test-template":
name = colorTheme.testTemplate().format("#" + segment.getValue());
break;
case "test-template-invocation":
name = colorTheme.container().format(":" + segment.getValue());
break;
default:
name = segment.getValue();
break;
}

if (options.isTypesEnabled()) {
name = segment.getType() + ":" + name;
}

return name;
}

if (options.isTypesEnabled()) {
name = segment.getType() + ":" + name;
/*
* Formats a test identifier run by junit-vintage engine.
*/
private String toVintageName(TestIdentifier identifier, Segment lastSegment) {

final String type = lastSegment.getType();

if ("runner".equals(type)) {

String className = identifier.getDisplayName();
return colorClassName(className, colorTheme.container());
}

if ("test".equals(type)) {

final TestSource source = identifier.getSource().orElse(null);

if (null == source) {
// Caused by Parameterized test runner, display name usually is the index name in brackets.
// Ignored since the index name is repeated in the display name of the test method.
return null;
}

if (source instanceof ClassSource) {
String nestedClassName = "$" + identifier.getDisplayName().replaceFirst(".*?\\$", "");
return colorTheme.container().format(nestedClassName);
}

if (source instanceof MethodSource) {
String testName = "#" + identifier.getDisplayName();
return colorTheme.testMethod().format(testName);
}
}

return "/" + identifier.getDisplayName();
}

return name;
/*
* Colors the last part of <className> with the given <color>.
*/
private String colorClassName(String className, Color color) {

String[] parts = className.split("\\.");
return IntStream.range(0, parts.length).mapToObj(i -> {
if (i == (parts.length - 1))
return color.format(parts[i]);
return parts[i];

}).collect(Collectors.joining("."));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,10 @@ public void executionStarted(TestIdentifier testIdentifier) {
startTimes.putIfAbsent(testIdentifier.getUniqueId(), System.currentTimeMillis());

if (!testIdentifier.getParentId().isPresent()) {
String message = "Test run started (" + testIdentifier.getDisplayName() + ")";
debugOrInfo(colorTheme.info().format(message));
if (!testPlan.getChildren(testIdentifier).isEmpty()) {
String message = "Test run started (" + testIdentifier.getDisplayName() + ")";
debugOrInfo(colorTheme.info().format(message));
}
}

if (testIdentifier.isTest()) {
Expand Down
72 changes: 47 additions & 25 deletions src/library/src/test/java/jupiter/TestHelper.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package jupiter;

import org.junit.platform.engine.TestSource;
import org.junit.platform.engine.support.descriptor.MethodSource;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.TestIdentifier;
import org.junit.platform.launcher.TestPlan;
import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.core.LauncherFactory;

import java.util.Collections;
import java.util.Set;
import java.util.stream.Collectors;

Expand All @@ -17,44 +18,65 @@
*/
public class TestHelper {

private TestPlan testPlan;
/**
* Creates a new test plan helper for a specific test class.
*
* @param testClass The name of the test class.
* @return A new test helper instance.
*/
public static TestHelper of(String testClass) {

LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectClass(testClass))
.build();

public Set<TestIdentifier> descendantsOfFirstRoot() {
final TestPlan testPlan = LauncherFactory.create().discover(request);

return testPlan.getRoots().stream().findAny()
.map(testPlan::getDescendants)
.orElseGet(Collections::emptySet);
return new TestHelper(testPlan);
}

public TestIdentifier findByName(String testName) {
public TestIdentifier findByMethodName(String testMethodName) {

final Set<TestIdentifier> descendantIdentifiers = getAllDescendants();
return descendantIdentifiers.stream()
.filter(testIdentifier -> {

TestSource testSource = testIdentifier.getSource().orElse(null);
if (testSource instanceof MethodSource) {
return ((MethodSource) testSource).getMethodName().equals(testMethodName);
}
return false;
})
.findAny()
.orElseThrow(() -> {
String message = "Could not find test method " + testMethodName + " in:\n";
String identifiers = descendantIdentifiers.stream()
.map(TestIdentifier::getUniqueId)
.collect(Collectors.joining(",\n"));

return descendantsOfFirstRoot().stream()
.filter(identifier -> testName.equals(identifier.getLegacyReportingName()))
.findAny().orElseThrow(() -> {
String message = "Could not find test " + testName + " in:\n";
String identifiers = descendantsOfFirstRoot().stream()
.map(TestIdentifier::getLegacyReportingName)
.collect(Collectors.joining("\n"));
return new AssertionError(message + identifiers);
});
}

public TestHelper loadTestClass(String className) {
public TestPlan testPlan() {
return testPlan;
}

LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
.selectors(selectClass(className))
.build();
final private TestPlan testPlan;

private TestHelper(TestPlan testPlan) {

testPlan = LauncherFactory.create().discover(request);
return this;
this.testPlan = testPlan;
}

/**
* @see #loadTestClass(String)
* @return The test plan
* @return Descendant test identifiers from all available roots (test engines).
*/
public TestPlan testPlan() {
return testPlan;
}
private Set<TestIdentifier> getAllDescendants() {

return testPlan.getRoots().stream()
.map(r -> testPlan().getDescendants(r).stream())
.flatMap(s -> s)
.collect(Collectors.toSet());
}
}
Loading

0 comments on commit 51755b6

Please sign in to comment.