ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jaikiran <...@git.apache.org>
Subject [GitHub] ant pull request #60: JUnit 5 support - A new junitlauncher task
Date Wed, 21 Feb 2018 12:45:03 GMT
Github user jaikiran commented on a diff in the pull request:

    https://github.com/apache/ant/pull/60#discussion_r169624837
  
    --- Diff: src/main/org/apache/tools/ant/taskdefs/optional/junitlauncher/JUnitLauncherTask.java
---
    @@ -0,0 +1,508 @@
    +package org.apache.tools.ant.taskdefs.optional.junitlauncher;
    +
    +import org.apache.tools.ant.AntClassLoader;
    +import org.apache.tools.ant.BuildException;
    +import org.apache.tools.ant.Project;
    +import org.apache.tools.ant.Task;
    +import org.apache.tools.ant.types.Path;
    +import org.apache.tools.ant.util.KeepAliveOutputStream;
    +import org.junit.platform.launcher.Launcher;
    +import org.junit.platform.launcher.LauncherDiscoveryRequest;
    +import org.junit.platform.launcher.TestExecutionListener;
    +import org.junit.platform.launcher.TestPlan;
    +import org.junit.platform.launcher.core.LauncherFactory;
    +import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
    +import org.junit.platform.launcher.listeners.TestExecutionSummary;
    +
    +import java.io.Closeable;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.io.OutputStream;
    +import java.io.PipedInputStream;
    +import java.io.PipedOutputStream;
    +import java.io.PrintStream;
    +import java.nio.file.Files;
    +import java.nio.file.Paths;
    +import java.util.ArrayList;
    +import java.util.Collection;
    +import java.util.Collections;
    +import java.util.List;
    +import java.util.Optional;
    +import java.util.concurrent.BlockingQueue;
    +import java.util.concurrent.CountDownLatch;
    +import java.util.concurrent.LinkedBlockingQueue;
    +import java.util.concurrent.TimeUnit;
    +
    +/**
    + * An Ant {@link Task} responsible for launching the JUnit platform for running tests.
    + * This requires a minimum of JUnit 5, since that's the version in which the JUnit platform
launcher
    + * APIs were introduced.
    + * <p>
    + * This task in itself doesn't run the JUnit tests, instead the sole responsibility of
    + * this task is to setup the JUnit platform launcher, build requests, launch those requests
and then parse the
    + * result of the execution to present in a way that's been configured on this Ant task.
    + * </p>
    + * <p>
    + * Furthermore, this task allows users control over which classes to select for passing
on to the JUnit 5
    + * platform for test execution. It however, is solely the JUnit 5 platform, backed by
test engines that
    + * decide and execute the tests.
    + *
    + * @see <a href="https://junit.org/junit5/">JUnit 5 documentation</a> for
more details
    + * on how JUnit manages the platform and the test engines.
    + */
    +public class JUnitLauncherTask extends Task {
    +
    +    private Path classPath;
    +    private boolean haltOnFailure;
    +    private String failureProperty;
    +    private final List<TestDefinition> tests = new ArrayList<>();
    +    private final List<ListenerDefinition> listeners = new ArrayList<>();
    +
    +    public JUnitLauncherTask() {
    +    }
    +
    +    @Override
    +    public void execute() throws BuildException {
    +        final ClassLoader previousClassLoader = Thread.currentThread().getContextClassLoader();
    +        try {
    +            final ClassLoader executionCL = createClassLoaderForTestExecution();
    +            Thread.currentThread().setContextClassLoader(executionCL);
    +            final Launcher launcher = LauncherFactory.create();
    +            final List<TestRequest> requests = buildTestRequests();
    +            for (final TestRequest testRequest : requests) {
    +                try {
    +                    final TestDefinition test = testRequest.getOwner();
    +                    final LauncherDiscoveryRequest request = testRequest.getDiscoveryRequest().build();
    +                    final List<TestExecutionListener> testExecutionListeners =
new ArrayList<>();
    +                    // a listener that we always put at the front of list of listeners
    +                    // for this request.
    +                    final Listener firstListener = new Listener();
    +                    // we always enroll the summary generating listener, to the request,
so that we
    +                    // get to use some of the details of the summary for our further
decision making
    +                    testExecutionListeners.add(firstListener);
    +                    testExecutionListeners.addAll(getListeners(testRequest, executionCL));
    +                    final PrintStream originalSysOut = System.out;
    +                    final PrintStream originalSysErr = System.err;
    +                    try {
    +                        firstListener.switchedSysOutHandle = trySwitchSysOut(testRequest);
    +                        firstListener.switchedSysErrHandle = trySwitchSysErr(testRequest);
    +                        launcher.execute(request, testExecutionListeners.toArray(new
TestExecutionListener[testExecutionListeners.size()]));
    +                    } finally {
    +                        // switch back sysout/syserr to the original
    +                        try {
    +                            System.setOut(originalSysOut);
    +                        } catch (Exception e) {
    +                            // ignore
    +                        }
    +                        try {
    +                            System.setErr(originalSysErr);
    +                        } catch (Exception e) {
    +                            // ignore
    +                        }
    +                    }
    +                    handleTestExecutionCompletion(test, firstListener.getSummary());
    +                } finally {
    +                    try {
    +                        testRequest.close();
    +                    } catch (Exception e) {
    +                        // log and move on
    +                        log("Failed to cleanly close test request", e, Project.MSG_DEBUG);
    +                    }
    +                }
    +            }
    +        } finally {
    +            Thread.currentThread().setContextClassLoader(previousClassLoader);
    +        }
    +    }
    +
    +    /**
    +     * @return Creates and returns the a {@link Path} which will be used as the classpath
of this
    +     * task. This classpath will then be used for execution of the tests
    +     */
    +    public Path createClassPath() {
    +        this.classPath = new Path(getProject());
    +        return this.classPath;
    +    }
    +
    +    /**
    +     * @return Creates and returns a {@link SingleTestClass}. This test will be considered
part of the
    +     * tests that will be passed on to the underlying JUnit platform for possible execution
of the test
    +     */
    +    public SingleTestClass createTest() {
    +        final SingleTestClass test = new SingleTestClass();
    +        this.preConfigure(test);
    --- End diff --
    
    Thank you, for catching this. I went back and read the Ant manual about writing custom
tasks (which I had skipped the first time around) and now understand this logic of execution
better. I have updated the PR to fix this and use the `addConfiguredXXX` construct and also
made sure my `preConfigure` implementation doesn't overwrite values that might have been specified
on the nested elements.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


Mime
View raw message