mas/MasKitTests/OutputListener.swift

85 lines
2.8 KiB
Swift
Raw Normal View History

2019-01-08 01:33:03 +00:00
//
// OutputListener.swift
// MasKitTests
//
// Created by Ben Chatelain on 1/7/19.
// Copyright © 2019 mas-cli. All rights reserved.
//
import Foundation
2019-01-08 22:34:23 +00:00
/// Test helper for monitoring strings written to stdout. Modified from:
2019-01-08 01:33:03 +00:00
/// https://medium.com/@thesaadismail/eavesdropping-on-swifts-print-statements-57f0215efb42
class OutputListener {
2019-01-08 22:34:23 +00:00
/// consumes the messages on STDOUT
2019-01-08 01:33:03 +00:00
let inputPipe = Pipe()
2019-01-08 22:34:23 +00:00
/// outputs messages back to STDOUT
2019-01-08 01:33:03 +00:00
let outputPipe = Pipe()
2019-01-08 22:34:23 +00:00
/// Buffers strings written to stdout
2019-01-08 01:33:03 +00:00
var contents = ""
init() {
// Set up a read handler which fires when data is written to our inputPipe
inputPipe.fileHandleForReading.readabilityHandler = { [weak self] fileHandle in
strongify(self) { context in
let data = fileHandle.availableData
if let string = String(data: data, encoding: String.Encoding.utf8) {
context.contents += string
}
// Write input back to stdout
context.outputPipe.fileHandleForWriting.write(data)
}
}
}
}
extension OutputListener {
2019-01-08 22:34:23 +00:00
/// Sets up the "tee" of piped output, intercepting stdout then passing it through.
///
/// ## [dup2 documentation](https://linux.die.net/man/2/dup2)
/// `int dup2(int oldfd, int newfd);`
/// `dup2()` makes `newfd` be the copy of `oldfd`, closing `newfd` first if necessary.
2019-01-08 01:33:03 +00:00
func openConsolePipe() {
2019-01-08 22:34:23 +00:00
var dupStatus: Int32
2019-01-08 01:33:03 +00:00
2019-01-08 22:34:23 +00:00
// Copy STDOUT file descriptor to outputPipe for writing strings back to STDOUT
dupStatus = dup2(stdoutFileDescriptor, outputPipe.fileHandleForWriting.fileDescriptor)
// Status should equal newfd
assert(dupStatus == outputPipe.fileHandleForWriting.fileDescriptor)
2019-01-08 01:33:03 +00:00
2019-01-08 22:34:23 +00:00
// Intercept STDOUT with inputPipe
2019-01-12 00:33:41 +00:00
// newFileDescriptor is the pipe's file descriptor and the old file descriptor is STDOUT_FILENO
2019-01-08 22:34:23 +00:00
dupStatus = dup2(inputPipe.fileHandleForWriting.fileDescriptor, stdoutFileDescriptor)
// Status should equal newfd
assert(dupStatus == stdoutFileDescriptor)
2019-01-08 01:33:03 +00:00
2019-01-08 22:34:23 +00:00
// Don't have any tests on stderr yet
// dup2(inputPipe.fileHandleForWriting.fileDescriptor, stderr)
2019-01-08 01:33:03 +00:00
}
2019-01-08 22:34:23 +00:00
/// Tears down the "tee" of piped output.
func closeConsolePipe() {
// Restore stdout
freopen("/dev/stdout", "a", stdout)
2019-01-08 01:33:03 +00:00
2019-01-08 22:34:23 +00:00
[inputPipe.fileHandleForReading, outputPipe.fileHandleForWriting].forEach { file in
file.closeFile()
}
}
}
2019-01-08 01:33:03 +00:00
2019-01-08 22:34:23 +00:00
extension OutputListener {
/// File descriptor for stdout (aka STDOUT_FILENO)
var stdoutFileDescriptor: Int32 {
return FileHandle.standardOutput.fileDescriptor
}
2019-01-08 01:33:03 +00:00
2019-01-08 22:34:23 +00:00
/// File descriptor for stderr (aka STDERR_FILENO)
var stderrFileDescriptor: Int32 {
return FileHandle.standardError.fileDescriptor
2019-01-08 01:33:03 +00:00
}
}