Skip to content
This repository has been archived by the owner on Jul 9, 2021. It is now read-only.

Commit

Permalink
Discover and execute TestNG-based test methods
Browse files Browse the repository at this point in the history
Execution of discovered methods is a best-effort solution. Should
be transformed to use TestNG entry-point correctly.
  • Loading branch information
sormuras committed Aug 28, 2018
1 parent a89fb0d commit a47d5fa
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 8 deletions.
12 changes: 6 additions & 6 deletions src/it/basic/verify.bsh
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ String log = new String(Files.readAllBytes(basedir.toPath().resolve("build.log")
String[] snippets = new String[] {
"[INFO] Launching JUnit Platform...",
"[INFO] Test run finished",
"[INFO] [ 1 containers found ]",
"[INFO] [ 2 containers found ]", // 1 engine + 1 class
"[INFO] [ 0 containers skipped ]",
"[INFO] [ 1 containers started ]",
"[INFO] [ 2 containers started ]",
"[INFO] [ 0 containers aborted ]",
"[INFO] [ 1 containers successful ]",
"[INFO] [ 2 containers successful ]",
"[INFO] [ 0 containers failed ]",
"[INFO] [ 0 tests found ]",
"[INFO] [ 2 tests found ]", // 2 methods
"[INFO] [ 0 tests skipped ]",
"[INFO] [ 0 tests started ]",
"[INFO] [ 2 tests started ]",
"[INFO] [ 0 tests aborted ]",
"[INFO] [ 0 tests successful ]",
"[INFO] [ 2 tests successful ]",
"[INFO] [ 0 tests failed ]",
"[INFO] BUILD SUCCESS"
};
Expand Down
55 changes: 55 additions & 0 deletions src/main/java/org/testng/junit5/DiscoveryHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.testng.junit5;

import static org.junit.platform.commons.util.ReflectionUtils.findAllClassesInClasspathRoot;
import static org.junit.platform.commons.util.ReflectionUtils.findAllClassesInPackage;

import java.util.Collection;
import java.util.function.BiConsumer;
import org.junit.platform.commons.util.ClassFilter;
import org.junit.platform.engine.EngineDiscoveryRequest;
import org.junit.platform.engine.discovery.ClassSelector;
import org.junit.platform.engine.discovery.ClasspathRootSelector;
import org.junit.platform.engine.discovery.PackageSelector;
import org.junit.platform.engine.support.descriptor.EngineDescriptor;

/** This should be in org.junit.platform.engine.support... */
class DiscoveryHelper {

private final EngineDiscoveryRequest engineDiscoveryRequest;
private final ClassFilter classFilter;

DiscoveryHelper(EngineDiscoveryRequest engineDiscoveryRequest, ClassFilter classFilter) {
this.engineDiscoveryRequest = engineDiscoveryRequest;
this.classFilter = classFilter;
}

void discover(EngineDescriptor engine, BiConsumer<EngineDescriptor, Class<?>> handler) {
// class-path root
engineDiscoveryRequest
.getSelectorsByType(ClasspathRootSelector.class)
.stream()
.map(ClasspathRootSelector::getClasspathRoot)
.map(uri -> findAllClassesInClasspathRoot(uri, classFilter))
.flatMap(Collection::stream)
.forEach(candidate -> handler.accept(engine, candidate));

// package
engineDiscoveryRequest
.getSelectorsByType(PackageSelector.class)
.stream()
.map(PackageSelector::getPackageName)
.map(packageName -> findAllClassesInPackage(packageName, classFilter))
.flatMap(Collection::stream)
.forEach(candidate -> handler.accept(engine, candidate));

// class
engineDiscoveryRequest
.getSelectorsByType(ClassSelector.class)
.stream()
.map(ClassSelector::getJavaClass)
.filter(classFilter)
.forEach(candidate -> handler.accept(engine, candidate));

// TODO Add missing selector types...
}
}
40 changes: 40 additions & 0 deletions src/main/java/org/testng/junit5/NGClassDescriptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package org.testng.junit5;

import static org.junit.platform.commons.util.ReflectionUtils.isAbstract;
import static org.junit.platform.commons.util.ReflectionUtils.isInnerClass;
import static org.junit.platform.commons.util.ReflectionUtils.isPublic;

import org.junit.platform.engine.TestSource;
import org.junit.platform.engine.UniqueId;
import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;
import org.junit.platform.engine.support.descriptor.ClassSource;

public class NGClassDescriptor extends AbstractTestDescriptor {

static boolean isCandidate(Class<?> candidate) {
if (!isPublic(candidate)) {
return false;
}
if (isAbstract(candidate)) {
return false;
}
if (isInnerClass(candidate)) {
return false;
}
return true;
}

static NGClassDescriptor newContainerDescriptor(UniqueId container, Class<?> candidate) {
var id = container.append("testng-class", candidate.getTypeName());
return new NGClassDescriptor(id, candidate.getSimpleName(), ClassSource.from(candidate));
}

private NGClassDescriptor(UniqueId uniqueId, String displayName, TestSource source) {
super(uniqueId, displayName, source);
}

@Override
public Type getType() {
return Type.CONTAINER;
}
}
30 changes: 30 additions & 0 deletions src/main/java/org/testng/junit5/NGMethodDescriptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.testng.junit5;

import java.lang.reflect.Method;
import org.junit.platform.engine.UniqueId;
import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;
import org.junit.platform.engine.support.descriptor.MethodSource;

public class NGMethodDescriptor extends AbstractTestDescriptor {

static NGMethodDescriptor newMethodDescriptor(UniqueId container, Method method) {
var id = container.append("testng-method", method.getName());
return new NGMethodDescriptor(id, method);
}

private final Method method;

private NGMethodDescriptor(UniqueId uniqueId, Method method) {
super(uniqueId, method.getName(), MethodSource.from(method));
this.method = method;
}

@Override
public Type getType() {
return Type.TEST;
}

public Method getMethod() {
return method;
}
}
43 changes: 41 additions & 2 deletions src/main/java/org/testng/junit5/TestNGine.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
package org.testng.junit5;

import static org.junit.platform.engine.support.filter.ClasspathScanningSupport.buildClassNamePredicate;

import java.util.Arrays;
import java.util.Optional;
import org.junit.platform.commons.util.ClassFilter;
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.TestExecutionResult;
import org.junit.platform.engine.UniqueId;
import org.junit.platform.engine.support.descriptor.EngineDescriptor;
import org.testng.annotations.Test;

public class TestNGine implements TestEngine {

Expand All @@ -19,15 +24,30 @@ public String getId() {
return ENGINE_ID;
}

public TestDescriptor discover(EngineDiscoveryRequest engineDiscoveryRequest, UniqueId uniqueId) {
public TestDescriptor discover(EngineDiscoveryRequest request, UniqueId uniqueId) {
var engine = new EngineDescriptor(uniqueId, ENGINE_DISPLAY_NAME);
// inspect "engineDiscoveryRequest" selectors and filters passed by the user
// inspect "request" selectors and filters passed by the user
// find TestNG-based test containers (classes) and tests (methods)
// wrap each in a new TestDescriptor
// add the created descriptor in a tree, below the "engine" descriptor
var filter = ClassFilter.of(buildClassNamePredicate(request), NGClassDescriptor::isCandidate);
var helper = new DiscoveryHelper(request, filter);
helper.discover(engine, this::handle);
return engine;
}

private void handle(EngineDescriptor engine, Class<?> candidate) {
var container = NGClassDescriptor.newContainerDescriptor(engine.getUniqueId(), candidate);
Arrays.stream(candidate.getMethods())
.filter(method -> method.isAnnotationPresent(Test.class))
.map(method -> NGMethodDescriptor.newMethodDescriptor(container.getUniqueId(), method))
.forEach(container::addChild);
if (container.getChildren().isEmpty()) {
return;
}
engine.addChild(container);
}

public void execute(ExecutionRequest request) {
var engine = request.getRootTestDescriptor();
var listener = request.getEngineExecutionListener();
Expand All @@ -36,9 +56,28 @@ public void execute(ExecutionRequest request) {
// 1. tell the listener we started
// 2. try to execute the container/test and evaluate its result
// 3. tell the listener about the test execution result
for (var classDescriptor : engine.getChildren()) {
listener.executionStarted(classDescriptor);
for (var methodDescriptor : classDescriptor.getChildren()) {
listener.executionStarted(methodDescriptor);
var result = executeMethod((NGMethodDescriptor) methodDescriptor);
listener.executionFinished(methodDescriptor, result);
}
listener.executionFinished(classDescriptor, TestExecutionResult.successful());
}
listener.executionFinished(engine, TestExecutionResult.successful());
}

private TestExecutionResult executeMethod(NGMethodDescriptor descriptor) {
try {
var target = descriptor.getMethod().getDeclaringClass().getConstructor().newInstance();
descriptor.getMethod().invoke(target);
} catch (ReflectiveOperationException e) {
return TestExecutionResult.failed(e);
}
return TestExecutionResult.successful();
}

public Optional<String> getGroupId() {
return Optional.of("org.testng");
}
Expand Down

0 comments on commit a47d5fa

Please sign in to comment.