11
11
package org .junit .platform .reporting .open .xml ;
12
12
13
13
import static org .apiguardian .api .API .Status .EXPERIMENTAL ;
14
+ import static org .junit .platform .commons .util .StringUtils .isNotBlank ;
14
15
import static org .junit .platform .reporting .open .xml .JUnitFactory .legacyReportingName ;
15
16
import static org .junit .platform .reporting .open .xml .JUnitFactory .type ;
16
17
import static org .junit .platform .reporting .open .xml .JUnitFactory .uniqueId ;
30
31
import static org .opentest4j .reporting .events .core .CoreFactory .tags ;
31
32
import static org .opentest4j .reporting .events .core .CoreFactory .uriSource ;
32
33
import static org .opentest4j .reporting .events .core .CoreFactory .userName ;
34
+ import static org .opentest4j .reporting .events .git .GitFactory .branch ;
35
+ import static org .opentest4j .reporting .events .git .GitFactory .commit ;
36
+ import static org .opentest4j .reporting .events .git .GitFactory .repository ;
37
+ import static org .opentest4j .reporting .events .git .GitFactory .status ;
33
38
import static org .opentest4j .reporting .events .java .JavaFactory .classSource ;
34
39
import static org .opentest4j .reporting .events .java .JavaFactory .classpathResourceSource ;
35
40
import static org .opentest4j .reporting .events .java .JavaFactory .fileEncoding ;
42
47
import static org .opentest4j .reporting .events .root .RootFactory .reported ;
43
48
import static org .opentest4j .reporting .events .root .RootFactory .started ;
44
49
50
+ import java .io .BufferedReader ;
45
51
import java .io .IOException ;
52
+ import java .io .InputStream ;
53
+ import java .io .InputStreamReader ;
54
+ import java .io .Reader ;
46
55
import java .io .UncheckedIOException ;
47
56
import java .net .InetAddress ;
48
57
import java .net .UnknownHostException ;
58
+ import java .nio .charset .Charset ;
49
59
import java .nio .file .Path ;
50
60
import java .time .Instant ;
51
61
import java .util .Map ;
62
+ import java .util .Optional ;
52
63
import java .util .concurrent .ConcurrentHashMap ;
64
+ import java .util .concurrent .TimeUnit ;
53
65
import java .util .concurrent .atomic .AtomicInteger ;
66
+ import java .util .function .BiConsumer ;
54
67
55
68
import org .apiguardian .api .API ;
56
69
import org .junit .platform .commons .JUnitException ;
70
+ import org .junit .platform .commons .util .ExceptionUtils ;
57
71
import org .junit .platform .commons .util .StringUtils ;
58
72
import org .junit .platform .engine .ConfigurationParameters ;
59
73
import org .junit .platform .engine .TestExecutionResult ;
74
88
import org .junit .platform .launcher .listeners .OutputDir ;
75
89
import org .opentest4j .reporting .events .api .DocumentWriter ;
76
90
import org .opentest4j .reporting .events .api .NamespaceRegistry ;
91
+ import org .opentest4j .reporting .events .core .Infrastructure ;
77
92
import org .opentest4j .reporting .events .core .Result ;
78
93
import org .opentest4j .reporting .events .core .Sources ;
79
94
import org .opentest4j .reporting .events .root .Events ;
@@ -103,6 +118,7 @@ public void testPlanExecutionStarted(TestPlan testPlan) {
103
118
if (isEnabled (config )) {
104
119
NamespaceRegistry namespaceRegistry = NamespaceRegistry .builder (Namespace .REPORTING_CORE ) //
105
120
.add ("e" , Namespace .REPORTING_EVENTS ) //
121
+ .add ("git" , Namespace .REPORTING_GIT ) //
106
122
.add ("java" , Namespace .REPORTING_JAVA ) //
107
123
.add ("junit" , JUnitFactory .NAMESPACE ,
108
124
"https://junit.org/junit5/schemas/open-test-reporting/junit-1.9.xsd" ) //
@@ -138,9 +154,92 @@ private void reportInfrastructure() {
138
154
.append (javaVersion (System .getProperty ("java.version" ))) //
139
155
.append (fileEncoding (System .getProperty ("file.encoding" ))) //
140
156
.append (heapSize (), heapSize -> heapSize .withMax (Runtime .getRuntime ().maxMemory ()));
157
+
158
+ addGitInfo (infrastructure );
141
159
});
142
160
}
143
161
162
+ private static void addGitInfo (Infrastructure infrastructure ) {
163
+ boolean gitInstalled = exec ("git" , "--version" ).isPresent ();
164
+ if (gitInstalled ) {
165
+ exec ("git" , "config" , "--get" , "remote.origin.url" ) //
166
+ .filter (StringUtils ::isNotBlank ) //
167
+ .ifPresent (
168
+ gitUrl -> infrastructure .append (repository (), repository -> repository .withOriginUrl (gitUrl )));
169
+ exec ("git" , "rev-parse" , "--abbrev-ref" , "HEAD" ) //
170
+ .filter (StringUtils ::isNotBlank ) //
171
+ .ifPresent (branch -> infrastructure .append (branch (branch )));
172
+ exec ("git" , "rev-parse" , "--verify" , "HEAD" ) //
173
+ .filter (StringUtils ::isNotBlank ) //
174
+ .ifPresent (gitCommitHash -> infrastructure .append (commit (gitCommitHash )));
175
+ exec ("git" , "status" , "--porcelain" ) //
176
+ .ifPresent (statusOutput -> infrastructure .append (status (statusOutput ),
177
+ status -> status .withClean (statusOutput .isEmpty ())));
178
+ }
179
+ }
180
+
181
+ static Optional <String > exec (String ... args ) {
182
+
183
+ Process process = startProcess (args );
184
+
185
+ try (Reader out = newBufferedReader (process .getInputStream ());
186
+ Reader err = newBufferedReader (process .getErrorStream ())) {
187
+
188
+ StringBuilder output = new StringBuilder ();
189
+ readAllChars (out , (chars , numChars ) -> output .append (chars , 0 , numChars ));
190
+
191
+ readAllChars (err , (__ , ___ ) -> {
192
+ // ignore
193
+ });
194
+
195
+ boolean terminated = process .waitFor (10 , TimeUnit .SECONDS );
196
+ return terminated && process .exitValue () == 0 ? Optional .of (trimAtEnd (output )) : Optional .empty ();
197
+ }
198
+ catch (InterruptedException e ) {
199
+ throw ExceptionUtils .throwAsUncheckedException (e );
200
+ }
201
+ catch (IOException ignore ) {
202
+ return Optional .empty ();
203
+ }
204
+ finally {
205
+ process .destroyForcibly ();
206
+ }
207
+ }
208
+
209
+ private static BufferedReader newBufferedReader (InputStream stream ) {
210
+ return new BufferedReader (new InputStreamReader (stream , Charset .defaultCharset ()));
211
+ }
212
+
213
+ private static Process startProcess (String [] args ) {
214
+ Process process ;
215
+ try {
216
+ process = Runtime .getRuntime ().exec (args );
217
+ }
218
+ catch (IOException e ) {
219
+ throw new RuntimeException (e );
220
+ }
221
+ return process ;
222
+ }
223
+
224
+ private static void readAllChars (Reader reader , BiConsumer <char [], Integer > consumer ) throws IOException {
225
+ char [] buffer = new char [1024 ];
226
+ int numChars ;
227
+ while ((numChars = reader .read (buffer )) != -1 ) {
228
+ consumer .accept (buffer , numChars );
229
+ }
230
+ }
231
+
232
+ private static String trimAtEnd (StringBuilder value ) {
233
+ int endIndex = value .length ();
234
+ for (int i = value .length () - 1 ; i >= 0 ; i --) {
235
+ if (Character .isWhitespace (value .charAt (i ))) {
236
+ endIndex --;
237
+ break ;
238
+ }
239
+ }
240
+ return value .substring (0 , endIndex );
241
+ }
242
+
144
243
@ Override
145
244
public void testPlanExecutionFinished (TestPlan testPlan ) {
146
245
try {
@@ -160,7 +259,7 @@ public void executionSkipped(TestIdentifier testIdentifier, String reason) {
160
259
reportStarted (testIdentifier , id );
161
260
eventsFileWriter .append (finished (id , Instant .now ()), //
162
261
finished -> finished .append (result (Result .Status .SKIPPED ), result -> {
163
- if (StringUtils . isNotBlank (reason )) {
262
+ if (isNotBlank (reason )) {
164
263
result .append (reason (reason ));
165
264
}
166
265
}));
0 commit comments