3232use PHPUnit \Event \Test \PreparationErrored ;
3333use PHPUnit \Event \Test \PreparationFailed ;
3434use PHPUnit \Event \Test \Prepared as TestStarted ;
35+ use PHPUnit \Event \Test \PrintedUnexpectedOutput ;
3536use PHPUnit \Event \Test \Skipped ;
3637use PHPUnit \Event \TestSuite \Skipped as TestSuiteSkipped ;
3738use PHPUnit \Event \TestSuite \Started as TestSuiteStarted ;
@@ -65,10 +66,16 @@ final class OtrXmlLogger
6566 /**
6667 * @var ?positive-int
6768 */
68- private ?int $ testId = null ;
69- private ?Throwable $ parentErrored = null ;
70- private ?Throwable $ parentFailed = null ;
71- private bool $ alreadyFinished = false ;
69+ private ?int $ testId = null ;
70+ private ?Throwable $ parentErrored = null ;
71+ private ?Throwable $ parentFailed = null ;
72+ private bool $ testWasPrepared = false ;
73+ private bool $ alreadyFinished = false ;
74+ private ?string $ unexpectedOutput = null ;
75+ private ?Status $ pendingStatus = null ;
76+ private ?string $ pendingReason = null ;
77+ private ?Throwable $ pendingThrowable = null ;
78+ private ?bool $ pendingAssertionError = null ;
7279 private bool $ includeGitInformation ;
7380
7481 /**
@@ -249,6 +256,15 @@ public function testPrepared(PreparationErrored|PreparationFailed|TestStarted $e
249256 $ this ->testId ,
250257 $ this ->parentId ,
251258 );
259+
260+ if ($ event instanceof TestStarted) {
261+ $ this ->testWasPrepared = true ;
262+ }
263+ }
264+
265+ public function testPrintedUnexpectedOutput (PrintedUnexpectedOutput $ event ): void
266+ {
267+ $ this ->unexpectedOutput = $ event ->output ();
252268 }
253269
254270 public function testFinished (): void
@@ -257,18 +273,63 @@ public function testFinished(): void
257273 $ this ->writer ->startElement ('e:finished ' );
258274 $ this ->writer ->writeAttribute ('id ' , (string ) $ this ->testId );
259275 $ this ->writer ->writeAttribute ('time ' , $ this ->timestamp ());
276+
277+ if ($ this ->unexpectedOutput !== null ) {
278+ $ this ->writer ->startElement ('attachments ' );
279+ $ this ->writer ->startElement ('output ' );
280+ $ this ->writer ->writeAttribute ('source ' , 'stdout ' );
281+ $ this ->writer ->writeAttribute ('time ' , $ this ->timestamp ());
282+ $ this ->writer ->writeCdata ($ this ->unexpectedOutput );
283+ $ this ->writer ->endElement ();
284+ $ this ->writer ->endElement ();
285+ }
286+
287+ $ status = Status::Successful;
288+
289+ if ($ this ->pendingStatus !== null ) {
290+ $ status = $ this ->pendingStatus ;
291+ }
292+
260293 $ this ->writer ->startElement ('result ' );
261- $ this ->writer ->writeAttribute ('status ' , Status::Successful->value );
294+ $ this ->writer ->writeAttribute ('status ' , $ status ->value );
295+
296+ if ($ this ->pendingReason !== null ) {
297+ $ this ->writer ->writeElement ('reason ' , $ this ->pendingReason );
298+ }
299+
300+ if ($ this ->pendingThrowable !== null ) {
301+ assert ($ this ->pendingAssertionError !== null );
302+
303+ $ this ->writeThrowable ($ this ->pendingThrowable , $ this ->pendingAssertionError );
304+ }
305+
262306 $ this ->writer ->endElement ();
263307 $ this ->writer ->endElement ();
308+
309+ $ this ->writer ->flush ();
264310 }
265311
266- $ this ->alreadyFinished = false ;
267- $ this ->testId = null ;
312+ $ this ->alreadyFinished = false ;
313+ $ this ->testWasPrepared = false ;
314+ $ this ->testId = null ;
315+ $ this ->unexpectedOutput = null ;
316+ $ this ->pendingStatus = null ;
317+ $ this ->pendingReason = null ;
318+ $ this ->pendingThrowable = null ;
319+ $ this ->pendingAssertionError = null ;
268320 }
269321
270322 public function testFailed (Failed $ event ): void
271323 {
324+ if ($ this ->testWasPrepared ) {
325+ $ this ->pendingStatus = Status::Failed;
326+ $ this ->pendingReason = $ event ->throwable ()->message ();
327+ $ this ->pendingThrowable = $ event ->throwable ();
328+ $ this ->pendingAssertionError = true ;
329+
330+ return ;
331+ }
332+
272333 $ this ->writer ->startElement ('e:finished ' );
273334 $ this ->writer ->writeAttribute ('id ' , (string ) $ this ->testId );
274335 $ this ->writer ->writeAttribute ('time ' , $ this ->timestamp ());
@@ -288,6 +349,15 @@ public function testFailed(Failed $event): void
288349
289350 public function testErrored (Errored $ event ): void
290351 {
352+ if ($ this ->testWasPrepared ) {
353+ $ this ->pendingStatus = Status::Errored;
354+ $ this ->pendingReason = $ event ->throwable ()->message ();
355+ $ this ->pendingThrowable = $ event ->throwable ();
356+ $ this ->pendingAssertionError = false ;
357+
358+ return ;
359+ }
360+
291361 $ this ->writer ->startElement ('e:finished ' );
292362 $ this ->writer ->writeAttribute ('id ' , (string ) $ this ->testId );
293363 $ this ->writer ->writeAttribute ('time ' , $ this ->timestamp ());
@@ -315,41 +385,35 @@ public function testSkipped(Skipped $event): void
315385 $ this ->testId ,
316386 $ this ->parentId ,
317387 );
318- }
319388
320- $ this ->writer ->startElement ('e:finished ' );
321- $ this ->writer ->writeAttribute ('id ' , (string ) $ this ->testId );
322- $ this ->writer ->writeAttribute ('time ' , $ this ->timestamp ());
323- $ this ->writer ->startElement ('result ' );
324- $ this ->writer ->writeAttribute ('status ' , Status::Skipped->value );
389+ $ this ->writer ->startElement ('e:finished ' );
390+ $ this ->writer ->writeAttribute ('id ' , (string ) $ this ->testId );
391+ $ this ->writer ->writeAttribute ('time ' , $ this ->timestamp ());
392+ $ this ->writer ->startElement ('result ' );
393+ $ this ->writer ->writeAttribute ('status ' , Status::Skipped->value );
325394
326- $ this ->writer ->writeElement ('reason ' , $ event ->message ());
395+ $ this ->writer ->writeElement ('reason ' , $ event ->message ());
327396
328- $ this ->writer ->endElement ();
329- $ this ->writer ->endElement ();
397+ $ this ->writer ->endElement ();
398+ $ this ->writer ->endElement ();
330399
331- $ this ->writer ->flush ();
400+ $ this ->writer ->flush ();
332401
333- $ this ->alreadyFinished = true ;
402+ $ this ->testId = null ;
403+
404+ return ;
405+ }
406+
407+ $ this ->pendingStatus = Status::Skipped;
408+ $ this ->pendingReason = $ event ->message ();
334409 }
335410
336411 public function markTestIncomplete (MarkedIncomplete $ event ): void
337412 {
338- $ this ->writer ->startElement ('e:finished ' );
339- $ this ->writer ->writeAttribute ('id ' , (string ) $ this ->testId );
340- $ this ->writer ->writeAttribute ('time ' , $ this ->timestamp ());
341- $ this ->writer ->startElement ('result ' );
342- $ this ->writer ->writeAttribute ('status ' , Status::Aborted->value );
343-
344- $ this ->writer ->writeElement ('reason ' , $ event ->throwable ()->message ());
345- $ this ->writeThrowable ($ event ->throwable (), false );
346-
347- $ this ->writer ->endElement ();
348- $ this ->writer ->endElement ();
349-
350- $ this ->writer ->flush ();
351-
352- $ this ->alreadyFinished = true ;
413+ $ this ->pendingStatus = Status::Aborted;
414+ $ this ->pendingReason = $ event ->throwable ()->message ();
415+ $ this ->pendingThrowable = $ event ->throwable ();
416+ $ this ->pendingAssertionError = false ;
353417 }
354418
355419 public function parentErrored (AfterLastTestMethodErrored |BeforeFirstTestMethodErrored $ event ): void
@@ -375,6 +439,7 @@ private function registerSubscribers(Facade $facade): void
375439 new TestPreparationErroredSubscriber ($ this ),
376440 new TestPreparationFailedSubscriber ($ this ),
377441 new TestPreparedSubscriber ($ this ),
442+ new TestPrintedUnexpectedOutputSubscriber ($ this ),
378443 new TestAbortedSubscriber ($ this ),
379444 new TestErroredSubscriber ($ this ),
380445 new TestFailedSubscriber ($ this ),
0 commit comments