From d3deebc9fd481ce03a5847ab852c5170dd87c43c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Tue, 27 Feb 2024 21:00:29 +0100 Subject: [PATCH] Fix process handling --- .../launch/MavenBuildConnectionProcess.java | 46 +- .../MavenBuildProjectDataConnection.java | 22 +- .../launch/testing/MavenTestRunnerClient.java | 77 +-- .../launch/testing/copied/IXMLTags.java | 213 +++--- .../launch/testing/copied/TestRunHandler.java | 612 ++++++++---------- 5 files changed, 467 insertions(+), 503 deletions(-) diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildConnectionProcess.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildConnectionProcess.java index 91e24a1ed9..37010abeba 100644 --- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildConnectionProcess.java +++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildConnectionProcess.java @@ -19,9 +19,10 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicBoolean; -import org.eclipse.core.runtime.Status; -import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugEvent; +import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.model.IProcess; import org.eclipse.debug.core.model.IStreamsProxy; @@ -49,10 +50,13 @@ public class MavenBuildConnectionProcess implements IProcess { private List buildListeners = new CopyOnWriteArrayList<>(); + private AtomicBoolean terminated = new AtomicBoolean(); + public MavenBuildConnectionProcess(ILaunch launch) { this.launch = launch; launch.addProcess(this); attributes.put(IProcess.ATTR_PROCESS_TYPE, "m2e-build-endpoint"); + fireEvent(new DebugEvent(this, DebugEvent.CREATE)); } public T getAdapter(Class adapter) { @@ -60,21 +64,27 @@ public T getAdapter(Class adapter) { } public boolean canTerminate() { - return true; + return !isTerminated(); } public boolean isTerminated() { - return connection == null || connection.isReadCompleted(); + return terminated.get() || connection == null; } - public void terminate() throws DebugException { - if(connection != null) { - try { - connection.close(); - } catch(IOException ex) { - throw new DebugException(Status.error("Terminate failed", ex)); + public void terminate() { + if(terminated.compareAndSet(false, true)) { + if(connection != null) { + try { + connection.close(); + } catch(IOException ex) { + } + connection = null; + } + for(MavenBuildListener mavenBuildListener : buildListeners) { + mavenBuildListener.close(); } - connection = null; + buildListeners.clear(); + fireEvent(new DebugEvent(this, DebugEvent.TERMINATE)); } } @@ -93,6 +103,7 @@ public IStreamsProxy getStreamsProxy() { @Override public void setAttribute(String key, String value) { attributes.put(key, value); + fireEvent(new DebugEvent(this, DebugEvent.CHANGE)); } @Override @@ -144,11 +155,7 @@ public void onTestEvent(MavenTestEvent mavenTestEvent) { } public void close() { - for(MavenBuildListener mavenBuildListener : buildListeners) { - mavenBuildListener.close(); - } - buildListeners.clear(); - launch.removeProcess(MavenBuildConnectionProcess.this); + MavenBuildConnectionProcess.this.terminate(); } }); } @@ -157,4 +164,11 @@ String getMavenVMArguments() throws IOException { return connection.getMavenVMArguments(); } + private void fireEvent(DebugEvent event) { + DebugPlugin manager = DebugPlugin.getDefault(); + if(manager != null) { + manager.fireDebugEventSet(new DebugEvent[] {event}); + } + } + } diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildProjectDataConnection.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildProjectDataConnection.java index 3ae45480fc..919caa77f1 100644 --- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildProjectDataConnection.java +++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildProjectDataConnection.java @@ -19,7 +19,6 @@ import java.util.Optional; import org.eclipse.core.runtime.CoreException; -import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchesListener2; @@ -36,17 +35,11 @@ public class MavenBuildProjectDataConnection { static { DebugPlugin.getDefault().getLaunchManager().addLaunchListener(new ILaunchesListener2() { public void launchesRemoved(ILaunch[] launches) { - Arrays.stream(launches).flatMap(launch -> getConnection(launch).stream()).forEach(con -> { - try { - con.terminate(); - } catch(DebugException ex) { - //ignore - } - }); + cleanupConnections(launches); } public void launchesTerminated(ILaunch[] launches) { - launchesRemoved(launches); + cleanupConnections(launches); } public void launchesAdded(ILaunch[] launches) { // ignore @@ -54,6 +47,12 @@ public void launchesAdded(ILaunch[] launches) { // ignore public void launchesChanged(ILaunch[] launches) { // ignore } + + private void cleanupConnections(ILaunch[] launches) { + Arrays.stream(launches).flatMap(launch -> getConnection(launch).stream()).forEach(con -> { + con.terminate(); + }); + } }); } @@ -61,10 +60,7 @@ static void openListenerConnection(ILaunch launch, VMArguments arguments) { try { if(MavenLaunchUtils.getMavenRuntime(launch.getLaunchConfiguration()) instanceof MavenEmbeddedRuntime) { getConnection(launch).ifPresent(existing -> { - try { - existing.terminate(); - } catch(DebugException ex) { - } + existing.terminate(); throw new IllegalStateException( "Maven bridge already created for launch of" + launch.getLaunchConfiguration().getName()); }); diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/MavenTestRunnerClient.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/MavenTestRunnerClient.java index 3bdeab71aa..e64b98d76f 100644 --- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/MavenTestRunnerClient.java +++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/MavenTestRunnerClient.java @@ -27,18 +27,14 @@ import org.xml.sax.SAXException; import org.eclipse.debug.core.ILaunch; -import org.eclipse.unittest.internal.junitXmlReport.TestRunHandler; import org.eclipse.unittest.launcher.ITestRunnerClient; -import org.eclipse.unittest.model.ITestCaseElement; -import org.eclipse.unittest.model.ITestElement; -import org.eclipse.unittest.model.ITestElement.FailureTrace; -import org.eclipse.unittest.model.ITestElement.Result; import org.eclipse.unittest.model.ITestRunSession; import org.eclipse.unittest.model.ITestSuiteElement; import org.apache.maven.execution.ExecutionEvent.Type; import org.eclipse.m2e.internal.launch.MavenBuildProjectDataConnection; +import org.eclipse.m2e.internal.launch.testing.copied.TestRunHandler; import org.eclipse.m2e.internal.maven.listener.MavenBuildListener; import org.eclipse.m2e.internal.maven.listener.MavenProjectBuildData; import org.eclipse.m2e.internal.maven.listener.MavenTestEvent; @@ -63,6 +59,7 @@ public MavenTestRunnerClient(ITestRunSession session) { } public void startMonitoring() { + System.out.println("---- start monitoring ---"); MavenBuildProjectDataConnection.getConnection(session.getLaunch()) .ifPresent(con -> con.addMavenBuildListener(this)); @@ -78,6 +75,7 @@ public void stopTest() { } public void stopMonitoring() { + System.out.println("---- stop Monitoring ----\r\n"); MavenBuildProjectDataConnection.getConnection(session.getLaunch()) .ifPresent(con -> con.removeMavenBuildListener(this)); } @@ -93,8 +91,14 @@ public void projectStarted(MavenProjectBuildData data) { this.projectData.set(data); } + boolean started; + public void onTestEvent(MavenTestEvent mavenTestEvent) { + System.out.println("MavenTestRunnerClient.onTestEvent()"); if(mavenTestEvent.getType() == Type.MojoSucceeded || mavenTestEvent.getType() == Type.MojoFailed) { + MavenProjectBuildData buildData = projectData.get(); +// Display.getDefault().execute(() -> { + //in any case look for the tests... Path reportDirectory = mavenTestEvent.getReportDirectory(); if(Files.isDirectory(reportDirectory)) { @@ -102,65 +106,43 @@ public void onTestEvent(MavenTestEvent mavenTestEvent) { if(parser == null) { return; } + ensureStarted(); + ITestSuiteElement projectSuite = getProjectSuite(buildData); try (Stream list = Files.list(reportDirectory)) { Iterator iterator = list.iterator(); while(iterator.hasNext()) { Path path = iterator.next(); System.out.println("Scan result file " + path); - ITestRunSession importedSession = parseFile(path, parser); - if(importedSession != null) { - ITestSuiteElement project = getProjectSuite(); - ITestSuiteElement file = session.newTestSuite(path.toString(), path.getFileName().toString(), null, - project, path.getFileName().toString(), null); - for(ITestElement element : importedSession.getChildren()) { - importTestElement(element, file); - } - } + parseFile(path, parser, projectSuite); } } catch(IOException ex) { } } +// }); } } - /** - * @param element - * @param file - */ - private void importTestElement(ITestElement element, ITestSuiteElement parent) { - if(element instanceof ITestCaseElement testcase) { - ITestCaseElement importedTestCase = session.newTestCase(parent.getId() + "." + testcase.getId(), - testcase.getTestName(), parent, testcase.getDisplayName(), testcase.getData()); - session.notifyTestStarted(importedTestCase); - FailureTrace failureTrace = testcase.getFailureTrace(); - if(failureTrace == null) { - session.notifyTestEnded(importedTestCase, testcase.isIgnored()); - } else { - session.notifyTestFailed(importedTestCase, Result.ERROR/*TODO how do we know?*/, false /*TODO how do we know?*/, - failureTrace); - } - } else if(element instanceof ITestSuiteElement suite) { - ITestSuiteElement importedTestSuite = session.newTestSuite(parent.getId() + "." + suite.getId(), - suite.getTestName(), null, parent, suite.getDisplayName(), suite.getData()); - session.notifyTestStarted(importedTestSuite); - for(ITestElement child : suite.getChildren()) { - importTestElement(child, importedTestSuite); - } - session.notifyTestEnded(importedTestSuite, false); + private synchronized void ensureStarted() { + if(!started) { + session.notifyTestSessionStarted(null); + started = true; } } /** * @return */ - private ITestSuiteElement getProjectSuite() { - return projectElementMap.computeIfAbsent(projectData.get(), data -> { + private ITestSuiteElement getProjectSuite(MavenProjectBuildData buildData) { + return projectElementMap.computeIfAbsent(buildData, data -> { Path basedir = data.projectBasedir; - return session.newTestSuite(basedir.toString(), basedir.getFileName().toString(), null, null, - data.groupId + ":" + data.artifactId, null); + ITestSuiteElement suite = session.newTestSuite(System.currentTimeMillis() + basedir.toString(), + basedir.getFileName().toString(), null, null, data.groupId + ":" + data.artifactId, null); + session.notifyTestStarted(suite); + return suite; }); } + @SuppressWarnings("restriction") private SAXParser getParser() { try { return org.eclipse.core.internal.runtime.XmlProcessorFactory.createSAXParserWithErrorOnDOCTYPE(); @@ -170,20 +152,19 @@ private SAXParser getParser() { return null; } - private ITestRunSession parseFile(Path path, SAXParser parser) { - //TODO Currently NOT working as this is internal API that is not exported! - final TestRunHandler handler = new TestRunHandler(); + private void parseFile(Path path, SAXParser parser, ITestSuiteElement parent) { + final TestRunHandler handler = new TestRunHandler(session, parent); try { parser.parse(Files.newInputStream(path), handler); - return handler.getTestRunSession(); } catch(SAXException | IOException ex) { //can't read then... - return null; } } public void close() { - // nothing to do yet... + if(started) { + session.notifyTestSessionCompleted(null); + } } } diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/copied/IXMLTags.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/copied/IXMLTags.java index efe388a47e..aaa636ee4d 100644 --- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/copied/IXMLTags.java +++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/copied/IXMLTags.java @@ -12,101 +12,130 @@ * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.unittest.internal.junitXmlReport; +package org.eclipse.m2e.internal.launch.testing.copied; interface IXMLTags { - String NODE_TESTRUN = "testrun"; //$NON-NLS-1$ - String NODE_TESTSUITES = "testsuites"; //$NON-NLS-1$ - String NODE_TESTSUITE = "testsuite"; //$NON-NLS-1$ - String NODE_PROPERTIES = "properties"; //$NON-NLS-1$ - String NODE_PROPERTY = "property"; //$NON-NLS-1$ - String NODE_TESTCASE = "testcase"; //$NON-NLS-1$ - String NODE_ERROR = "error"; //$NON-NLS-1$ - String NODE_FAILURE = "failure"; //$NON-NLS-1$ - String NODE_EXPECTED = "expected"; //$NON-NLS-1$ - String NODE_ACTUAL = "actual"; //$NON-NLS-1$ - String NODE_SYSTEM_OUT = "system-out"; //$NON-NLS-1$ - String NODE_SYSTEM_ERR = "system-err"; //$NON-NLS-1$ - String NODE_SKIPPED = "skipped"; //$NON-NLS-1$ - - /** - * value: String - */ - String ATTR_NAME = "name"; //$NON-NLS-1$ - /** - * value: String - */ - String ATTR_LAUNCH_CONFIG_NAME = "launch_config_name"; //$NON-NLS-1$ - /** - * value: Integer - */ - String ATTR_TESTS = "tests"; //$NON-NLS-1$ - /** - * value: Integer - */ - String ATTR_STARTED = "started"; //$NON-NLS-1$ - /** - * value: Integer - */ - String ATTR_FAILURES = "failures"; //$NON-NLS-1$ - /** - * value: Integer - */ - String ATTR_ERRORS = "errors"; //$NON-NLS-1$ - /** - * value: Boolean - */ - String ATTR_IGNORED = "ignored"; //$NON-NLS-1$ - /** - * value: String - */ - String ATTR_PACKAGE = "package"; //$NON-NLS-1$ - /** - * value: String - */ - String ATTR_ID = "id"; //$NON-NLS-1$ - /** - * value: String - */ - String ATTR_CLASSNAME = "classname"; //$NON-NLS-1$ - /** - * value: Boolean - */ - String ATTR_INCOMPLETE = "incomplete"; //$NON-NLS-1$ - /** - * value: Duration.toString() - */ - String ATTR_START_TIME = "startTime"; //$NON-NLS-1$ - /** - * value: Double (duration in seconds) - */ - String ATTR_DURATION = "time"; //$NON-NLS-1$ - /** - * value: String - */ - String ATTR_MESSAGE = "message"; //$NON-NLS-1$ - /** - * value: String - */ - String ATTR_DISPLAY_NAME = "displayname"; //$NON-NLS-1$ - /** - * value: Boolean - */ - String ATTR_DYNAMIC_TEST = "dynamicTest"; //$NON-NLS-1$ - /** - * value: String - */ - String ATTR_DATA = "data"; //$NON-NLS-1$ - - /** - * value: String - */ - String ATTR_INCLUDE_TAGS = "include_tags"; //$NON-NLS-1$ - /** - * value: String - */ - String ATTR_EXCLUDE_TAGS = "exclude_tags"; //$NON-NLS-1$ + String NODE_TESTRUN = "testrun"; //$NON-NLS-1$ + + String NODE_TESTSUITES = "testsuites"; //$NON-NLS-1$ + + String NODE_TESTSUITE = "testsuite"; //$NON-NLS-1$ + + String NODE_PROPERTIES = "properties"; //$NON-NLS-1$ + + String NODE_PROPERTY = "property"; //$NON-NLS-1$ + + String NODE_TESTCASE = "testcase"; //$NON-NLS-1$ + + String NODE_ERROR = "error"; //$NON-NLS-1$ + + String NODE_FAILURE = "failure"; //$NON-NLS-1$ + + String NODE_EXPECTED = "expected"; //$NON-NLS-1$ + + String NODE_ACTUAL = "actual"; //$NON-NLS-1$ + + String NODE_SYSTEM_OUT = "system-out"; //$NON-NLS-1$ + + String NODE_SYSTEM_ERR = "system-err"; //$NON-NLS-1$ + + String NODE_SKIPPED = "skipped"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_NAME = "name"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_LAUNCH_CONFIG_NAME = "launch_config_name"; //$NON-NLS-1$ + + /** + * value: Integer + */ + String ATTR_TESTS = "tests"; //$NON-NLS-1$ + + /** + * value: Integer + */ + String ATTR_STARTED = "started"; //$NON-NLS-1$ + + /** + * value: Integer + */ + String ATTR_FAILURES = "failures"; //$NON-NLS-1$ + + /** + * value: Integer + */ + String ATTR_ERRORS = "errors"; //$NON-NLS-1$ + + /** + * value: Boolean + */ + String ATTR_IGNORED = "ignored"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_PACKAGE = "package"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_ID = "id"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_CLASSNAME = "classname"; //$NON-NLS-1$ + + /** + * value: Boolean + */ + String ATTR_INCOMPLETE = "incomplete"; //$NON-NLS-1$ + + /** + * value: Duration.toString() + */ + String ATTR_START_TIME = "startTime"; //$NON-NLS-1$ + + /** + * value: Double (duration in seconds) + */ + String ATTR_DURATION = "time"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_MESSAGE = "message"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_DISPLAY_NAME = "displayname"; //$NON-NLS-1$ + + /** + * value: Boolean + */ + String ATTR_DYNAMIC_TEST = "dynamicTest"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_DATA = "data"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_INCLUDE_TAGS = "include_tags"; //$NON-NLS-1$ + + /** + * value: String + */ + String ATTR_EXCLUDE_TAGS = "exclude_tags"; //$NON-NLS-1$ // public static final String ATTR_TYPE= "type"; //$NON-NLS-1$ } diff --git a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/copied/TestRunHandler.java b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/copied/TestRunHandler.java index fc9dad0ba7..71a961d378 100644 --- a/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/copied/TestRunHandler.java +++ b/org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/testing/copied/TestRunHandler.java @@ -12,11 +12,10 @@ * IBM Corporation - initial API and implementation *******************************************************************************/ -package org.eclipse.unittest.internal.junitXmlReport; +package org.eclipse.m2e.internal.launch.testing.copied; -import java.time.Duration; -import java.time.Instant; import java.util.Stack; +import java.util.concurrent.atomic.AtomicInteger; import org.xml.sax.Attributes; import org.xml.sax.Locator; @@ -24,342 +23,287 @@ import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; -import org.eclipse.osgi.util.NLS; -import org.eclipse.unittest.internal.UnitTestPlugin; -import org.eclipse.unittest.internal.model.ModelMessages; -import org.eclipse.unittest.internal.model.TestCaseElement; -import org.eclipse.unittest.internal.model.TestElement; -import org.eclipse.unittest.internal.model.TestRunSession; -import org.eclipse.unittest.internal.model.TestSuiteElement; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.unittest.model.ITestCaseElement; import org.eclipse.unittest.model.ITestElement; import org.eclipse.unittest.model.ITestElement.FailureTrace; import org.eclipse.unittest.model.ITestElement.Result; +import org.eclipse.unittest.model.ITestRunSession; +import org.eclipse.unittest.model.ITestSuiteElement; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.NullProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; - -import org.eclipse.debug.core.DebugPlugin; -import org.eclipse.debug.core.ILaunchConfiguration; public class TestRunHandler extends DefaultHandler { - /* - * TODO: validate (currently assumes correct XML) - */ - - private int fId; - - private TestRunSession fTestRunSession; - private TestSuiteElement fTestSuite; - private TestCaseElement fTestCase; - private final Stack fNotRun = new Stack<>(); - - private StringBuilder fFailureBuffer; - private boolean fInExpected; - private boolean fInActual; - private StringBuilder fExpectedBuffer; - private StringBuilder fActualBuffer; - - private Locator fLocator; - - private Result fStatus; - - private final IProgressMonitor fMonitor; - private int fLastReportedLine; - - /** - * Constructs a default {@link TestRunHandler} object instance - */ - public TestRunHandler() { - fMonitor = new NullProgressMonitor(); - } - - /** - * Constructs a {@link TestRunHandler} object instance - * - * @param monitor a progress monitor - */ - public TestRunHandler(IProgressMonitor monitor) { - fMonitor = monitor != null ? monitor : new NullProgressMonitor(); - } - - @Override - public void setDocumentLocator(Locator locator) { - fLocator = locator; - } - - @Override - public void startDocument() throws SAXException { - // Nothing to do - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { - if (fMonitor.isCanceled()) - throw new OperationCanceledException(); - - if (fLocator != null) { - int line = fLocator.getLineNumber(); - if (line - 20 >= fLastReportedLine) { - line -= line % 20; - fLastReportedLine = line; - fMonitor.subTask(NLS.bind(ModelMessages.TestRunHandler_lines_read, Integer.valueOf(line))); - } - } - - if (Thread.interrupted()) - throw new OperationCanceledException(); - - switch (qName) { - case IXMLTags.NODE_TESTRUN: - if (fTestRunSession == null) { - String name = attributes.getValue(IXMLTags.ATTR_NAME); - String launchConfigName = attributes.getValue(IXMLTags.ATTR_LAUNCH_CONFIG_NAME); - ILaunchConfiguration launchConfiguration = null; - if (launchConfigName != null) { - try { - for (ILaunchConfiguration config : DebugPlugin.getDefault().getLaunchManager() - .getLaunchConfigurations()) { - if (config.getName().equals(launchConfigName)) { - launchConfiguration = config; - } - } - } catch (CoreException e) { - UnitTestPlugin.log(e); - } - } - fTestRunSession = new TestRunSession(name, Instant.parse(attributes.getValue(IXMLTags.ATTR_START_TIME)), - launchConfiguration); - readDuration(fTestRunSession, attributes); - // TODO: read counts? - } else { - fTestRunSession.reset(); - } - fTestSuite = fTestRunSession; - break; - case IXMLTags.NODE_TESTSUITES: - break; - case IXMLTags.NODE_TESTSUITE: { - String name = attributes.getValue(IXMLTags.ATTR_NAME); - String pack = attributes.getValue(IXMLTags.ATTR_PACKAGE); - String suiteName = pack == null ? name : pack + "." + name; //$NON-NLS-1$ - String displayName = attributes.getValue(IXMLTags.ATTR_DISPLAY_NAME); - String data = attributes.getValue(IXMLTags.ATTR_DATA); - if (data != null && data.isBlank()) { - data = null; - } - fTestSuite = (TestSuiteElement) fTestRunSession.createTestElement(fTestSuite, getNextId(), suiteName, true, - null, false, displayName, data); - readDuration(fTestSuite, attributes); - fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE))); - break; - } - // not interested - case IXMLTags.NODE_PROPERTIES: - case IXMLTags.NODE_PROPERTY: - break; - case IXMLTags.NODE_TESTCASE: { - String name = attributes.getValue(IXMLTags.ATTR_NAME); - String classname = attributes.getValue(IXMLTags.ATTR_CLASSNAME); - String testName = name + '(' + classname + ')'; - boolean isDynamicTest = Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_DYNAMIC_TEST)).booleanValue(); - String displayName = attributes.getValue(IXMLTags.ATTR_DISPLAY_NAME); - String data = attributes.getValue(IXMLTags.ATTR_DATA); - if (data != null && data.isBlank()) { - data = null; - } - fTestCase = (TestCaseElement) fTestRunSession.createTestElement(fTestSuite, getNextId(), testName, false, 1, - isDynamicTest, displayName, data); - fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE))); - fTestCase.setIgnored(Boolean.parseBoolean(attributes.getValue(IXMLTags.ATTR_IGNORED))); - readDuration(fTestCase, attributes); - break; - } - case IXMLTags.NODE_ERROR: - // TODO: multiple failures: https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296 - fStatus = Result.ERROR; - fFailureBuffer = new StringBuilder(); - break; - case IXMLTags.NODE_FAILURE: - // TODO: multiple failures: https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296 - fStatus = Result.FAILURE; - fFailureBuffer = new StringBuilder(); - break; - case IXMLTags.NODE_EXPECTED: - fInExpected = true; - fExpectedBuffer = new StringBuilder(); - break; - case IXMLTags.NODE_ACTUAL: - fInActual = true; - fActualBuffer = new StringBuilder(); - break; - // not interested - case IXMLTags.NODE_SYSTEM_OUT: - case IXMLTags.NODE_SYSTEM_ERR: - break; - case IXMLTags.NODE_SKIPPED: - // before Ant 1.9.0: not an Ant JUnit tag, see - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=276068 - // later: child of or , see - // https://issues.apache.org/bugzilla/show_bug.cgi?id=43969 - fStatus = Result.OK; - fFailureBuffer = new StringBuilder(); - String message = attributes.getValue(IXMLTags.ATTR_MESSAGE); - if (message != null) { - fFailureBuffer.append(message).append('\n'); - } - break; - default: - throw new SAXParseException("unknown node '" + qName + "'", fLocator); //$NON-NLS-1$//$NON-NLS-2$ - } - } - - private void readDuration(ITestElement testElement, Attributes attributes) { - if (testElement instanceof TestElement) { - TestElement element = (TestElement) testElement; - String timeString = attributes.getValue(IXMLTags.ATTR_DURATION); - if (timeString != null) { - try { - double seconds = Double.parseDouble(timeString); - long millis = (long) (seconds * 1000); - element.setDuration(Duration.ofMillis(millis)); - } catch (NumberFormatException e) { - // Ignore - } - } - } - } - - @Override - public void characters(char[] ch, int start, int length) throws SAXException { - if (fInExpected) { - fExpectedBuffer.append(ch, start, length); - - } else if (fInActual) { - fActualBuffer.append(ch, start, length); - - } else if (fFailureBuffer != null) { - fFailureBuffer.append(ch, start, length); - } - } - - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - switch (qName) { - // OK - case IXMLTags.NODE_TESTRUN: - break; - // OK - case IXMLTags.NODE_TESTSUITES: - break; - case IXMLTags.NODE_TESTSUITE: - handleTestElementEnd(fTestSuite); - fTestSuite = fTestSuite.getParent(); - // TODO: end suite: compare counters? - break; - // OK - case IXMLTags.NODE_PROPERTIES: - case IXMLTags.NODE_PROPERTY: - break; - case IXMLTags.NODE_TESTCASE: - handleTestElementEnd(fTestCase); - fTestCase = null; - break; - case IXMLTags.NODE_FAILURE: - case IXMLTags.NODE_ERROR: { - ITestElement testElement = fTestCase; - if (testElement == null) - testElement = fTestSuite; - handleFailure(testElement); - break; - } - case IXMLTags.NODE_EXPECTED: - fInExpected = false; - if (fFailureBuffer != null) { - // skip whitespace from before and nodes - fFailureBuffer.setLength(0); - } - break; - case IXMLTags.NODE_ACTUAL: - fInActual = false; - if (fFailureBuffer != null) { - // skip whitespace from before and nodes - fFailureBuffer.setLength(0); - } - break; - // OK - case IXMLTags.NODE_SYSTEM_OUT: - case IXMLTags.NODE_SYSTEM_ERR: - break; - case IXMLTags.NODE_SKIPPED: { - TestElement testElement = fTestCase; - if (testElement == null) - testElement = fTestSuite; - if (fFailureBuffer != null && fFailureBuffer.length() > 0) { - handleFailure(testElement); - testElement.setAssumptionFailed(true); - } else if (fTestCase != null) { - fTestCase.setIgnored(true); - } else { // not expected - testElement.setAssumptionFailed(true); - } - break; - } - default: - handleUnknownNode(qName); - break; - } - } - - private void handleTestElementEnd(ITestElement testElement) { - boolean completed = !fNotRun.pop().booleanValue(); - fTestRunSession.registerTestEnded((TestElement) testElement, completed); - } - - private void handleFailure(ITestElement testElement) { - if (fFailureBuffer != null) { - fTestRunSession.registerTestFailureStatus((TestElement) testElement, fStatus, - new FailureTrace(fFailureBuffer.toString(), toString(fExpectedBuffer), toString(fActualBuffer))); - fFailureBuffer = null; - fExpectedBuffer = null; - fActualBuffer = null; - fStatus = null; - } - } - - private String toString(StringBuilder buffer) { - return buffer != null ? buffer.toString() : null; - } - - private void handleUnknownNode(String qName) throws SAXException { - // TODO: just log if debug option is enabled? - String msg = "unknown node '" + qName + "'"; //$NON-NLS-1$//$NON-NLS-2$ - if (fLocator != null) { - msg += " at line " + fLocator.getLineNumber() + ", column " + fLocator.getColumnNumber(); //$NON-NLS-1$//$NON-NLS-2$ - } - throw new SAXException(msg); - } - - @Override - public void error(SAXParseException e) throws SAXException { - throw e; - } - - @Override - public void warning(SAXParseException e) throws SAXException { - throw e; - } - - private String getNextId() { - return Integer.toString(fId++); - } - - /** - * @return the parsed test run session, or null - */ - public TestRunSession getTestRunSession() { - return fTestRunSession; - } + private static final AtomicInteger fid = new AtomicInteger(); + + private ITestRunSession fTestRunSession; + + private ITestSuiteElement fTestSuite; + + private ITestCaseElement fTestCase; + + private final Stack fNotRun = new Stack<>(); + + private StringBuilder fFailureBuffer; + + private boolean fInExpected; + + private boolean fInActual; + + private StringBuilder fExpectedBuffer; + + private StringBuilder fActualBuffer; + + private Locator fLocator; + + private Result fStatus; + + private int fLastReportedLine; + + /** + * Constructs a default {@link TestRunHandler} object instance + */ + public TestRunHandler(ITestRunSession session, ITestSuiteElement parent) { + fTestRunSession = session; + this.fTestSuite = parent; + } + + @Override + public void setDocumentLocator(Locator locator) { + fLocator = locator; + } + + @Override + public void startDocument() throws SAXException { + // Nothing to do + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + try { + if(fLocator != null) { + int line = fLocator.getLineNumber(); + if(line - 20 >= fLastReportedLine) { + line -= line % 20; + fLastReportedLine = line; + } + } + + if(Thread.interrupted()) + throw new OperationCanceledException(); + + switch(qName) { + case IXMLTags.NODE_TESTRUN: + break; + case IXMLTags.NODE_TESTSUITES: + break; + case IXMLTags.NODE_TESTSUITE: { + String name = attributes.getValue(IXMLTags.ATTR_NAME); + String pack = attributes.getValue(IXMLTags.ATTR_PACKAGE); + String suiteName = pack == null ? name : pack + "." + name; //$NON-NLS-1$ + String displayName = attributes.getValue(IXMLTags.ATTR_DISPLAY_NAME); + String data = attributes.getValue(IXMLTags.ATTR_DATA); + if(data != null && data.isBlank()) { + data = null; + } + fTestSuite = fTestRunSession.newTestSuite(getNextId(), suiteName, null, fTestSuite, displayName, data); + fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE))); + fTestRunSession.notifyTestStarted(fTestSuite); + break; + } + // not interested + case IXMLTags.NODE_PROPERTIES: + case IXMLTags.NODE_PROPERTY: + break; + case IXMLTags.NODE_TESTCASE: { + String name = attributes.getValue(IXMLTags.ATTR_NAME); + String classname = attributes.getValue(IXMLTags.ATTR_CLASSNAME); + String testName = name + '(' + classname + ')'; + //TODO dynamic test not supported by API! +// boolean isDynamicTest = Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_DYNAMIC_TEST)).booleanValue(); + String displayName = attributes.getValue(IXMLTags.ATTR_DISPLAY_NAME); + String data = attributes.getValue(IXMLTags.ATTR_DATA); + if(data != null && data.isBlank()) { + data = null; + } + fTestCase = fTestRunSession.newTestCase(getNextId(), testName, fTestSuite, displayName, data); + fNotRun.push(Boolean.valueOf(attributes.getValue(IXMLTags.ATTR_INCOMPLETE))); + //TODO incomplete versus ignored?!? +// fTestCase.setIgnored(Boolean.parseBoolean(attributes.getValue(IXMLTags.ATTR_IGNORED))); + fTestRunSession.notifyTestStarted(fTestCase); + break; + } + case IXMLTags.NODE_ERROR: + // TODO: multiple failures: https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296 + fStatus = Result.ERROR; + fFailureBuffer = new StringBuilder(); + break; + case IXMLTags.NODE_FAILURE: + // TODO: multiple failures: https://bugs.eclipse.org/bugs/show_bug.cgi?id=125296 + fStatus = Result.FAILURE; + fFailureBuffer = new StringBuilder(); + break; + case IXMLTags.NODE_EXPECTED: + fInExpected = true; + fExpectedBuffer = new StringBuilder(); + break; + case IXMLTags.NODE_ACTUAL: + fInActual = true; + fActualBuffer = new StringBuilder(); + break; + // not interested + case IXMLTags.NODE_SYSTEM_OUT: + case IXMLTags.NODE_SYSTEM_ERR: + break; + case IXMLTags.NODE_SKIPPED: + // before Ant 1.9.0: not an Ant JUnit tag, see + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=276068 + // later: child of or , see + // https://issues.apache.org/bugzilla/show_bug.cgi?id=43969 + fStatus = Result.OK; + fFailureBuffer = new StringBuilder(); + String message = attributes.getValue(IXMLTags.ATTR_MESSAGE); + if(message != null) { + fFailureBuffer.append(message).append('\n'); + } + break; + default: + throw new SAXParseException("unknown node '" + qName + "'", fLocator); //$NON-NLS-1$//$NON-NLS-2$ + } + } catch(RuntimeException e) { + e.printStackTrace(); + } + } + + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + if(fInExpected) { + fExpectedBuffer.append(ch, start, length); + + } else if(fInActual) { + fActualBuffer.append(ch, start, length); + + } else if(fFailureBuffer != null) { + fFailureBuffer.append(ch, start, length); + } + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + try { + switch(qName) { + // OK + case IXMLTags.NODE_TESTRUN: + break; + // OK + case IXMLTags.NODE_TESTSUITES: + break; + case IXMLTags.NODE_TESTSUITE: + handleTestElementEnd(fTestSuite); + fTestSuite = fTestSuite.getParent(); + // TODO: end suite: compare counters? + break; + // OK + case IXMLTags.NODE_PROPERTIES: + case IXMLTags.NODE_PROPERTY: + break; + case IXMLTags.NODE_TESTCASE: + handleTestElementEnd(fTestCase); + fTestCase = null; + break; + case IXMLTags.NODE_FAILURE: + case IXMLTags.NODE_ERROR: { + ITestElement testElement = fTestCase; + if(testElement == null) + testElement = fTestSuite; + handleFailure(testElement, false); + break; + } + case IXMLTags.NODE_EXPECTED: + fInExpected = false; + if(fFailureBuffer != null) { + // skip whitespace from before and nodes + fFailureBuffer.setLength(0); + } + break; + case IXMLTags.NODE_ACTUAL: + fInActual = false; + if(fFailureBuffer != null) { + // skip whitespace from before and nodes + fFailureBuffer.setLength(0); + } + break; + // OK + case IXMLTags.NODE_SYSTEM_OUT: + case IXMLTags.NODE_SYSTEM_ERR: + break; + case IXMLTags.NODE_SKIPPED: { + ITestElement testElement = fTestCase; + if(testElement == null) { + testElement = fTestSuite; + } + if(fFailureBuffer != null && fFailureBuffer.length() > 0) { + handleFailure(testElement, true); + } else if(fTestCase != null) { + fStatus = Result.IGNORED; + } else { // not expected + fTestRunSession.notifyTestFailed(testElement, Result.FAILURE, false, null); + } + break; + } + default: + handleUnknownNode(qName); + break; + } + } catch(RuntimeException e) { + e.printStackTrace(); + } + } + + private void handleTestElementEnd(ITestElement testElement) { + boolean notrun = fNotRun.pop().booleanValue(); + if(notrun) { + return; + } + fTestRunSession.notifyTestEnded(testElement, fStatus == Result.IGNORED); + } + + private void handleFailure(ITestElement testElement, boolean assumptionFailed) { + if(fFailureBuffer != null) { + fTestRunSession.notifyTestFailed(testElement, fStatus, assumptionFailed, + new FailureTrace(fFailureBuffer.toString(), toString(fExpectedBuffer), toString(fActualBuffer))); + fFailureBuffer = null; + fExpectedBuffer = null; + fActualBuffer = null; + fStatus = null; + } + } + + private String toString(StringBuilder buffer) { + return buffer != null ? buffer.toString() : null; + } + + private void handleUnknownNode(String qName) throws SAXException { + // TODO: just log if debug option is enabled? + String msg = "unknown node '" + qName + "'"; //$NON-NLS-1$//$NON-NLS-2$ + if(fLocator != null) { + msg += " at line " + fLocator.getLineNumber() + ", column " + fLocator.getColumnNumber(); //$NON-NLS-1$//$NON-NLS-2$ + } + throw new SAXException(msg); + } + + @Override + public void error(SAXParseException e) throws SAXException { + throw e; + } + + @Override + public void warning(SAXParseException e) throws SAXException { + throw e; + } + + private String getNextId() { + return fTestSuite.getId() + "." + fid.incrementAndGet(); + } + }