@@ -530,6 +530,73 @@ extension ABI {
530530
531531@_spi ( ForToolsIntegrationOnly)
532532extension ExitTest {
533+ /// A barrier value to insert into the standard output and standard error
534+ /// streams immediately before and after the body of an exit test runs in
535+ /// order to distinguish output produced by the host process.
536+ ///
537+ /// The value of this property was randomly generated. It could conceivably
538+ /// show up in actual output from an exit test, but the statistical likelihood
539+ /// of that happening is negligible.
540+ static var barrierValue : [ UInt8 ] {
541+ [
542+ 0x39 , 0x74 , 0x87 , 0x6d , 0x96 , 0xdd , 0xf6 , 0x17 ,
543+ 0x7f , 0x05 , 0x61 , 0x5d , 0x46 , 0xeb , 0x37 , 0x0c ,
544+ 0x90 , 0x07 , 0xca , 0xe5 , 0xed , 0x0b , 0xc4 , 0xc4 ,
545+ 0x46 , 0x36 , 0xc5 , 0xb8 , 0x9c , 0xc7 , 0x86 , 0x57 ,
546+ ]
547+ }
548+
549+ /// Remove the leading and trailing barrier values from the given array of
550+ /// bytes along.
551+ ///
552+ /// - Parameters:
553+ /// - buffer: The buffer to trim.
554+ ///
555+ /// - Returns: A copy of `buffer`. If a barrier value (equal to
556+ /// ``barrierValue``) is present in `buffer`, it and everything before it
557+ /// are trimmed from the beginning of the copy. If there is more than one
558+ /// barrier value present, the last one and everything after it are trimmed
559+ /// from the end of the copy. If no barrier value is present, `buffer` is
560+ /// returned verbatim.
561+ private static func _trimToBarrierValues( _ buffer: [ UInt8 ] ) -> [ UInt8 ] {
562+ let barrierValue = barrierValue
563+ let firstBarrierByte = barrierValue [ 0 ]
564+
565+ // If the buffer is too small to contain the barrier value, exit early.
566+ guard buffer. count > barrierValue. count else {
567+ return buffer
568+ }
569+
570+ // Find all the indices where the first byte of the barrier is present.
571+ let splits = buffer. indices. filter { buffer [ $0] == firstBarrierByte }
572+
573+ // Trim off the leading barrier value. If we didn't find any barrier values,
574+ // we do nothing.
575+ let leadingIndex = splits. first { buffer [ $0... ] . starts ( with: barrierValue) }
576+ guard let leadingIndex else {
577+ return buffer
578+ }
579+ var trimmedBuffer = buffer [ leadingIndex... ] . dropFirst ( barrierValue. count)
580+
581+ // If there's a trailing barrier value, trim it too. If it's at the same
582+ // index as the leading barrier value, that means only one barrier value
583+ // was present and we should assume it's the leading one.
584+ let trailingIndex = splits. last { buffer [ $0... ] . starts ( with: barrierValue) }
585+ if let trailingIndex, trailingIndex > leadingIndex {
586+ trimmedBuffer = trimmedBuffer [ ..< trailingIndex]
587+ }
588+
589+ return Array ( trimmedBuffer)
590+ }
591+
592+ /// Write barrier values (equal to ``barrierValue``) to the standard output
593+ /// and standard error streams of the current process.
594+ private static func _writeBarrierValues( ) {
595+ let barrierValue = Self . barrierValue
596+ try ? FileHandle . stdout. write ( barrierValue)
597+ try ? FileHandle . stderr. write ( barrierValue)
598+ }
599+
533600 /// A handler that is invoked when an exit test starts.
534601 ///
535602 /// - Parameters:
@@ -682,6 +749,13 @@ extension ExitTest {
682749 }
683750
684751 result. body = { [ configuration, body = result. body] exitTest in
752+ Self . _writeBarrierValues ( )
753+ defer {
754+ // We will generally not end up writing these values if the process
755+ // exits abnormally.
756+ Self . _writeBarrierValues ( )
757+ }
758+
685759 try await Configuration . withCurrent ( configuration) {
686760 try exitTest. _decodeCapturedValuesForEntryPoint ( )
687761 try await body ( & exitTest)
@@ -862,14 +936,14 @@ extension ExitTest {
862936 if let stdoutReadEnd {
863937 stdoutWriteEnd? . close ( )
864938 taskGroup. addTask {
865- let standardOutputContent = try stdoutReadEnd. readToEnd ( )
939+ let standardOutputContent = try Self . _trimToBarrierValues ( stdoutReadEnd. readToEnd ( ) )
866940 return { $0. standardOutputContent = standardOutputContent }
867941 }
868942 }
869943 if let stderrReadEnd {
870944 stderrWriteEnd? . close ( )
871945 taskGroup. addTask {
872- let standardErrorContent = try stderrReadEnd. readToEnd ( )
946+ let standardErrorContent = try Self . _trimToBarrierValues ( stderrReadEnd. readToEnd ( ) )
873947 return { $0. standardErrorContent = standardErrorContent }
874948 }
875949 }
0 commit comments