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 = ""
|
|
|
|
|
2019-01-09 05:23:38 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
}
|