Skip to content

Commit 50518ae

Browse files
committed
Trace test execution directories in the remote maven execution
Currently running tests in maven via m2e is possible but not very convenient as one still needs to navigate to the results then open the correct file. Another pitfall is that even if one has opened the file once, the "classic" JUnit view not update the view even when the file changes afterwards. This now adds a new process tracking of test executions directories that then can be watched on the m2e side and display the new advanced JUnit view when the run has finished.
1 parent 8789e4c commit 50518ae

File tree

10 files changed

+424
-92
lines changed

10 files changed

+424
-92
lines changed

org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenBuildProjectDataConnection.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
import org.eclipse.m2e.internal.launch.MavenRuntimeLaunchSupport.VMArguments;
3131
import org.eclipse.m2e.internal.maven.listener.M2EMavenBuildDataBridge;
3232
import org.eclipse.m2e.internal.maven.listener.M2EMavenBuildDataBridge.MavenBuildConnection;
33-
import org.eclipse.m2e.internal.maven.listener.M2EMavenBuildDataBridge.MavenProjectBuildData;
33+
import org.eclipse.m2e.internal.maven.listener.MavenBuildListener;
34+
import org.eclipse.m2e.internal.maven.listener.MavenProjectBuildData;
35+
import org.eclipse.m2e.internal.maven.listener.MavenTestEvent;
3436

3537

3638
public class MavenBuildProjectDataConnection {
@@ -74,9 +76,21 @@ static void openListenerConnection(ILaunch launch, VMArguments arguments) {
7476

7577
Map<ArtifactKey, MavenProjectBuildData> projects = new ConcurrentHashMap<>();
7678

77-
MavenBuildConnection connection = M2EMavenBuildDataBridge.prepareConnection(
78-
launch.getLaunchConfiguration().getName(),
79-
d -> projects.put(new ArtifactKey(d.groupId, d.artifactId, d.version, null), d));
79+
MavenBuildConnection connection = M2EMavenBuildDataBridge
80+
.prepareConnection(launch.getLaunchConfiguration().getName(), new MavenBuildListener() {
81+
82+
@Override
83+
public void projectStarted(MavenProjectBuildData data) {
84+
projects.put(new ArtifactKey(data.groupId, data.artifactId, data.version, null), data);
85+
}
86+
87+
@Override
88+
public void onTestEvent(MavenTestEvent mavenTestEvent) {
89+
System.out.println(
90+
"Test event " + mavenTestEvent.getType() + " for directory " + mavenTestEvent.getReportDirectory());
91+
92+
}
93+
});
8094

8195
if(LAUNCH_PROJECT_DATA.putIfAbsent(launch, new MavenBuildConnectionData(projects, connection)) != null) {
8296
connection.close();

org.eclipse.m2e.launching/src/org/eclipse/m2e/internal/launch/MavenConsoleLineTracker.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565

6666
import org.eclipse.m2e.core.internal.IMavenConstants;
6767
import org.eclipse.m2e.core.project.IBuildProjectFileResolver;
68-
import org.eclipse.m2e.internal.maven.listener.M2EMavenBuildDataBridge.MavenProjectBuildData;
68+
import org.eclipse.m2e.internal.maven.listener.MavenProjectBuildData;
6969

7070

7171
/**

org.eclipse.m2e.maven.runtime/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
</parent>
2121

2222
<artifactId>org.eclipse.m2e.maven.runtime</artifactId>
23-
<version>3.9.600-SNAPSHOT</version>
23+
<version>3.9.601-SNAPSHOT</version>
2424
<packaging>jar</packaging>
2525

2626
<name>M2E Embedded Maven Runtime (includes Incubating components)</name>

org.eclipse.m2e.maven.runtime/src/main/java/org/eclipse/m2e/internal/maven/listener/M2EMavenBuildDataBridge.java

Lines changed: 44 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,26 @@
2121
import java.nio.ByteBuffer;
2222
import java.nio.channels.ServerSocketChannel;
2323
import java.nio.channels.SocketChannel;
24-
import java.nio.file.Path;
2524
import java.nio.file.Paths;
26-
import java.util.HashMap;
27-
import java.util.Map;
28-
import java.util.Objects;
29-
import java.util.StringJoiner;
3025
import java.util.concurrent.atomic.AtomicBoolean;
31-
import java.util.function.Consumer;
3226

3327
import javax.inject.Named;
3428
import javax.inject.Singleton;
3529

36-
import org.apache.maven.eventspy.EventSpy;
37-
import org.apache.maven.execution.ExecutionEvent;
30+
import org.apache.maven.AbstractMavenLifecycleParticipant;
31+
import org.apache.maven.MavenExecutionException;
3832
import org.apache.maven.execution.ExecutionEvent.Type;
39-
import org.apache.maven.project.MavenProject;
33+
import org.apache.maven.execution.MavenSession;
4034
import org.slf4j.Logger;
4135
import org.slf4j.LoggerFactory;
4236

4337
/**
44-
* This {@link EventSpy} listens to certain events within a Maven build JVM and
45-
* sends certain data (e.g. about projects being built) to the JVM of the
46-
* Eclipse IDE that launched the Maven build JVM.
47-
*
48-
* @author Hannes Wellmann
49-
*
38+
* Bridge between the remote running maven and the local m2e to exchange event
39+
* messages and information.
5040
*/
51-
@Named
41+
@Named("m2e")
5242
@Singleton
53-
public class M2EMavenBuildDataBridge implements EventSpy {
43+
public class M2EMavenBuildDataBridge extends AbstractMavenLifecycleParticipant {
5444

5545
private static final String SOCKET_FILE_PROPERTY_NAME = "m2e.build.project.data.socket.port";
5646
private static final String DATA_SET_SEPARATOR = ";;";
@@ -60,7 +50,7 @@ public class M2EMavenBuildDataBridge implements EventSpy {
6050
private SocketChannel writeChannel;
6151

6252
@Override
63-
public void init(Context context) throws IOException {
53+
public void afterSessionStart(MavenSession session) throws MavenExecutionException {
6454
String socketPort = System.getProperty(SOCKET_FILE_PROPERTY_NAME);
6555
if (socketPort != null) {
6656
try {
@@ -76,81 +66,38 @@ public void init(Context context) throws IOException {
7666
}
7767

7868
@Override
79-
public void close() throws IOException {
80-
writeChannel.close();
69+
public void afterSessionEnd(MavenSession session) throws MavenExecutionException {
70+
try {
71+
writeChannel.close();
72+
} catch (IOException e) {
73+
// we can't do much here anyways...
74+
}
8175
}
8276

83-
@Override
84-
public void onEvent(Object event) throws Exception {
85-
if (writeChannel != null && event instanceof ExecutionEvent
86-
&& ((ExecutionEvent) event).getType() == Type.ProjectStarted) {
87-
88-
String message = serializeProjectData(((ExecutionEvent) event).getProject());
77+
boolean isActive() {
78+
return writeChannel != null;
79+
}
8980

90-
ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
91-
synchronized (writeChannel) {
81+
void sendMessage(String msg) {
82+
SocketChannel channel = writeChannel;
83+
if (channel != null) {
84+
ByteBuffer buffer = ByteBuffer.wrap((msg + DATA_SET_SEPARATOR).getBytes());
85+
synchronized (channel) {
9286
while (buffer.hasRemaining()) {
93-
writeChannel.write(buffer);
87+
try {
88+
channel.write(buffer);
89+
} catch (IOException e) {
90+
LOGGER.warn("Can't forward message to m2e: " + e);
91+
}
9492
}
9593
}
9694
}
9795
}
9896

99-
private static String serializeProjectData(MavenProject project) {
100-
StringJoiner data = new StringJoiner(",");
101-
add(data, "groupId", project.getGroupId());
102-
add(data, "artifactId", project.getArtifactId());
103-
add(data, "version", project.getVersion());
104-
add(data, "file", project.getFile());
105-
add(data, "basedir", project.getBasedir());
106-
add(data, "build.directory", project.getBuild().getDirectory());
107-
return data.toString() + DATA_SET_SEPARATOR;
108-
}
10997

110-
private static void add(StringJoiner data, String key, Object value) {
111-
data.add(key + "=" + value);
112-
}
11398

114-
/**
115-
* <p>
116-
* This method is supposed to be called from M2E within the Eclipse-IDE JVM.
117-
* </p>
118-
*
119-
* @param dataSet the data-set to parse
120-
* @return the {@link MavenProjectBuildData} parsed from the given string
121-
*/
122-
private static MavenProjectBuildData parseMavenBuildProject(String dataSet) {
123-
Map<String, String> data = new HashMap<>(8);
124-
for (String entry : dataSet.split(",")) {
125-
String[] keyValue = entry.split("=");
126-
if (keyValue.length != 2) {
127-
throw new IllegalStateException("Invalid data-set format" + dataSet);
128-
}
129-
data.put(keyValue[0], keyValue[1]);
130-
}
131-
return new MavenProjectBuildData(data);
132-
}
13399

134-
public static final class MavenProjectBuildData {
135-
public final String groupId;
136-
public final String artifactId;
137-
public final String version;
138-
public final Path projectBasedir;
139-
public final Path projectFile;
140-
public final Path projectBuildDirectory;
141-
142-
MavenProjectBuildData(Map<String, String> data) {
143-
if (data.size() != 6) {
144-
throw new IllegalArgumentException();
145-
}
146-
this.groupId = Objects.requireNonNull(data.get("groupId"));
147-
this.artifactId = Objects.requireNonNull(data.get("artifactId"));
148-
this.version = Objects.requireNonNull(data.get("version"));
149-
this.projectBasedir = Paths.get(data.get("basedir"));
150-
this.projectFile = Paths.get(data.get("file"));
151-
this.projectBuildDirectory = Paths.get(data.get("build.directory"));
152-
}
153-
}
100+
154101

155102
/**
156103
* Prepares the connection to a {@code Maven build JVM} to be launched and is
@@ -163,7 +110,7 @@ public static final class MavenProjectBuildData {
163110
* @return the preapre {@link MavenBuildConnection}
164111
* @throws IOException
165112
*/
166-
public static MavenBuildConnection prepareConnection(String label, Consumer<MavenProjectBuildData> datasetListener)
113+
public static MavenBuildConnection prepareConnection(String label, MavenBuildListener datasetListener)
167114
throws IOException {
168115

169116
// TODO: use UNIX domain socket once Java-17 is required by Maven
@@ -187,9 +134,21 @@ public static MavenBuildConnection prepareConnection(String label, Consumer<Mave
187134
for (int terminatorIndex; (terminatorIndex = message.indexOf(DATA_SET_SEPARATOR)) >= 0;) {
188135
String dataSet = message.substring(0, terminatorIndex);
189136
message.delete(0, terminatorIndex + DATA_SET_SEPARATOR.length());
190-
191-
MavenProjectBuildData buildData = parseMavenBuildProject(dataSet);
192-
datasetListener.accept(buildData);
137+
if (dataSet.startsWith(M2eEventSpy.PROJECT_START_EVENT)) {
138+
MavenProjectBuildData buildData = MavenProjectBuildData
139+
.parseMavenBuildProject(
140+
dataSet.substring(M2eEventSpy.PROJECT_START_EVENT.length()));
141+
datasetListener.projectStarted(buildData);
142+
} else if (dataSet.startsWith(M2eMojoExecutionListener.TEST_START_EVENT)) {
143+
String path = dataSet.substring(M2eMojoExecutionListener.TEST_START_EVENT.length());
144+
datasetListener.onTestEvent(new MavenTestEvent(Type.MojoStarted, Paths.get(path)));
145+
} else if (dataSet.startsWith(M2eMojoExecutionListener.TEST_END_EVENT)) {
146+
String path = dataSet.substring(M2eMojoExecutionListener.TEST_END_EVENT.length());
147+
datasetListener.onTestEvent(new MavenTestEvent(Type.MojoSucceeded, Paths.get(path)));
148+
} else if (dataSet.startsWith(M2eMojoExecutionListener.TEST_END_FAILED_EVENT)) {
149+
String path = dataSet.substring(M2eMojoExecutionListener.TEST_END_EVENT.length());
150+
datasetListener.onTestEvent(new MavenTestEvent(Type.MojoFailed, Paths.get(path)));
151+
}
193152
}
194153
// Explicit cast for compatibility with covariant return type on JDK 9's
195154
// ByteBuffer
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/********************************************************************************
2+
* Copyright (c) 2022, 2024 Hannes Wellmann and others
3+
*
4+
* This program and the accompanying materials are made available under the
5+
* terms of the Eclipse Public License 2.0 which is available at
6+
* http://www.eclipse.org/legal/epl-2.0.
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Hannes Wellmann - initial API and implementation
12+
* Christoph Läubrich - factored out
13+
********************************************************************************/
14+
15+
package org.eclipse.m2e.internal.maven.listener;
16+
17+
import javax.inject.Named;
18+
import javax.inject.Singleton;
19+
20+
import org.apache.maven.eventspy.EventSpy;
21+
import org.apache.maven.execution.ExecutionEvent;
22+
import org.apache.maven.execution.ExecutionEvent.Type;
23+
24+
import com.google.inject.Inject;
25+
26+
/**
27+
* This {@link EventSpy} listens to certain events within a Maven build JVM and
28+
* sends certain data (e.g. about projects being built) to the JVM of the
29+
* Eclipse IDE that launched the Maven build JVM.
30+
*
31+
* @author Hannes Wellmann
32+
*
33+
*/
34+
@Named("m2e")
35+
@Singleton
36+
public class M2eEventSpy implements EventSpy {
37+
38+
private M2EMavenBuildDataBridge bridge;
39+
static final String PROJECT_START_EVENT = "PSE#";
40+
41+
@Inject
42+
public M2eEventSpy(M2EMavenBuildDataBridge bridge) {
43+
this.bridge = bridge;
44+
}
45+
46+
@Override
47+
public void init(Context context) throws Exception {
48+
49+
}
50+
51+
@Override
52+
public void onEvent(Object event) throws Exception {
53+
if (!bridge.isActive()) {
54+
return;
55+
}
56+
if (event instanceof ExecutionEvent) {
57+
ExecutionEvent executionEvent = (ExecutionEvent) event;
58+
if (executionEvent.getType() == Type.ProjectStarted) {
59+
String message = M2eEventSpy.PROJECT_START_EVENT
60+
+ MavenProjectBuildData.serializeProjectData(executionEvent.getProject());
61+
bridge.sendMessage(message);
62+
}
63+
}
64+
}
65+
66+
@Override
67+
public void close() throws Exception {
68+
69+
}
70+
71+
}

0 commit comments

Comments
 (0)