@@ -545,6 +545,73 @@ extension ABI {
545545
546546@_spi ( ForToolsIntegrationOnly)
547547extension ExitTest {
548+ /// A barrier value to insert into the standard output and standard error
549+ /// streams immediately before and after the body of an exit test runs in
550+ /// order to distinguish output produced by the host process.
551+ ///
552+ /// The value of this property was randomly generated. It could conceivably
553+ /// show up in actual output from an exit test, but the statistical likelihood
554+ /// of that happening is negligible.
555+ static var barrierValue : [ UInt8 ] {
556+ [
557+ 0x39 , 0x74 , 0x87 , 0x6d , 0x96 , 0xdd , 0xf6 , 0x17 ,
558+ 0x7f , 0x05 , 0x61 , 0x5d , 0x46 , 0xeb , 0x37 , 0x0c ,
559+ 0x90 , 0x07 , 0xca , 0xe5 , 0xed , 0x0b , 0xc4 , 0xc4 ,
560+ 0x46 , 0x36 , 0xc5 , 0xb8 , 0x9c , 0xc7 , 0x86 , 0x57 ,
561+ ]
562+ }
563+
564+ /// Remove the leading and trailing barrier values from the given array of
565+ /// bytes along.
566+ ///
567+ /// - Parameters:
568+ /// - buffer: The buffer to trim.
569+ ///
570+ /// - Returns: A copy of `buffer`. If a barrier value (equal to
571+ /// ``barrierValue``) is present in `buffer`, it and everything before it
572+ /// are trimmed from the beginning of the copy. If there is more than one
573+ /// barrier value present, the last one and everything after it are trimmed
574+ /// from the end of the copy. If no barrier value is present, `buffer` is
575+ /// returned verbatim.
576+ private static func _trimToBarrierValues( _ buffer: [ UInt8 ] ) -> [ UInt8 ] {
577+ let barrierValue = barrierValue
578+ let firstBarrierByte = barrierValue [ 0 ]
579+
580+ // If the buffer is too small to contain the barrier value, exit early.
581+ guard buffer. count > barrierValue. count else {
582+ return buffer
583+ }
584+
585+ // Find all the indices where the first byte of the barrier is present.
586+ let splits = buffer. indices. filter { buffer [ $0] == firstBarrierByte }
587+
588+ // Trim off the leading barrier value. If we didn't find any barrier values,
589+ // we do nothing.
590+ let leadingIndex = splits. first { buffer [ $0... ] . starts ( with: barrierValue) }
591+ guard let leadingIndex else {
592+ return buffer
593+ }
594+ var trimmedBuffer = buffer [ leadingIndex... ] . dropFirst ( barrierValue. count)
595+
596+ // If there's a trailing barrier value, trim it too. If it's at the same
597+ // index as the leading barrier value, that means only one barrier value
598+ // was present and we should assume it's the leading one.
599+ let trailingIndex = splits. last { buffer [ $0... ] . starts ( with: barrierValue) }
600+ if let trailingIndex, trailingIndex > leadingIndex {
601+ trimmedBuffer = trimmedBuffer [ ..< trailingIndex]
602+ }
603+
604+ return Array ( trimmedBuffer)
605+ }
606+
607+ /// Write barrier values (equal to ``barrierValue``) to the standard output
608+ /// and standard error streams of the current process.
609+ private static func _writeBarrierValues( ) {
610+ let barrierValue = Self . barrierValue
611+ try ? FileHandle . stdout. write ( barrierValue)
612+ try ? FileHandle . stderr. write ( barrierValue)
613+ }
614+
548615 /// A handler that is invoked when an exit test starts.
549616 ///
550617 /// - Parameters:
@@ -697,6 +764,13 @@ extension ExitTest {
697764 }
698765
699766 result. body = { [ configuration, body = result. body] exitTest in
767+ Self . _writeBarrierValues ( )
768+ defer {
769+ // We will generally not end up writing these values if the process
770+ // exits abnormally.
771+ Self . _writeBarrierValues ( )
772+ }
773+
700774 try await Configuration . withCurrent ( configuration) {
701775 try exitTest. _decodeCapturedValuesForEntryPoint ( )
702776 try await body ( & exitTest)
@@ -877,14 +951,14 @@ extension ExitTest {
877951 if let stdoutReadEnd {
878952 stdoutWriteEnd? . close ( )
879953 taskGroup. addTask {
880- let standardOutputContent = try stdoutReadEnd. readToEnd ( )
954+ let standardOutputContent = try Self . _trimToBarrierValues ( stdoutReadEnd. readToEnd ( ) )
881955 return { $0. standardOutputContent = standardOutputContent }
882956 }
883957 }
884958 if let stderrReadEnd {
885959 stderrWriteEnd? . close ( )
886960 taskGroup. addTask {
887- let standardErrorContent = try stderrReadEnd. readToEnd ( )
961+ let standardErrorContent = try Self . _trimToBarrierValues ( stderrReadEnd. readToEnd ( ) )
888962 return { $0. standardErrorContent = standardErrorContent }
889963 }
890964 }
0 commit comments