@@ -545,6 +545,73 @@ extension ABI {
545
545
546
546
@_spi ( ForToolsIntegrationOnly)
547
547
extension 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
+
548
615
/// A handler that is invoked when an exit test starts.
549
616
///
550
617
/// - Parameters:
@@ -697,6 +764,13 @@ extension ExitTest {
697
764
}
698
765
699
766
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
+
700
774
try await Configuration . withCurrent ( configuration) {
701
775
try exitTest. _decodeCapturedValuesForEntryPoint ( )
702
776
try await body ( & exitTest)
@@ -877,14 +951,14 @@ extension ExitTest {
877
951
if let stdoutReadEnd {
878
952
stdoutWriteEnd? . close ( )
879
953
taskGroup. addTask {
880
- let standardOutputContent = try stdoutReadEnd. readToEnd ( )
954
+ let standardOutputContent = try Self . _trimToBarrierValues ( stdoutReadEnd. readToEnd ( ) )
881
955
return { $0. standardOutputContent = standardOutputContent }
882
956
}
883
957
}
884
958
if let stderrReadEnd {
885
959
stderrWriteEnd? . close ( )
886
960
taskGroup. addTask {
887
- let standardErrorContent = try stderrReadEnd. readToEnd ( )
961
+ let standardErrorContent = try Self . _trimToBarrierValues ( stderrReadEnd. readToEnd ( ) )
888
962
return { $0. standardErrorContent = standardErrorContent }
889
963
}
890
964
}
0 commit comments