Eviscerate pretty much everything.

Will be changing a lot of the API, and do not want to have to change all the unit test and the rest of the code for every single change.
This commit is contained in:
Kare Morstol
2015-07-19 22:49:39 +02:00
parent 9ba2b1f186
commit 8bec5cf499
15 changed files with 3 additions and 934 deletions
-71
View File
@@ -16,74 +16,3 @@ public func runLater (shellcommand: String) -> NSTask {
return task
}
// TODO: explain thoroughly why this local state monstrosity is necessary.
// Summary: it is used exclusively for sending a stream through the forward operator and use it as standardInput in a run command.
// Required to enable "func run (shellcommand: String) -> ReadableStreamType" to also be run by itself on a single line.
private var _nextinput_: ReadableStreamType?
/**
Specific handling of func run (shellcommand: String) -> ReadableStreamType on the right side using the stream on the
left side as standard input.
Warning: is only meant to be used with the "run" command, but could also unintentionally catch other uses of
"ReadableStreamType |> ReadableStreamType", though that statement doesn't make any sense.
*/
public func |> (lhs: ReadableStreamType, @autoclosure rhs: () -> ReadableStreamType) -> ReadableStreamType {
assert(_nextinput_ == nil)
_nextinput_ = lhs
let result = rhs()
if _nextinput_ != nil {
printErrorAndExit("The statement 'ReadableStreamType |> ReadableStreamType' is invalid.")
}
return result
}
/**
Run a shell command synchronously with no standard input,
or if to the right of a "ReadableStreamType |> ", use the stream on the left side as standard input.
- returns: Standard output
*/
public func run (shellcommand: String) -> ReadableStreamType {
let task = runLater(shellcommand)
if let input = _nextinput_ {
task.standardInput = input as! FileHandle
_nextinput_ = nil
} else {
// avoids implicit reading of the main script's standardInput
task.standardInput = NSPipe ()
}
let output = NSPipe ()
task.standardOutput = output
task.launch()
// necessary for now to ensure one shellcommand is finished before another begins.
// uncontrolled asynchronous shell processes could be messy.
// but shell commands on the same line connected with the pipe operator should preferably be asynchronous.
task.waitUntilExit()
return output.fileHandleForReading
}
/** Shortcut for in-line command, returns output as String. */
public func $ (shellcommand: String) -> String {
let task = runLater(shellcommand)
// avoids implicit reading of the main script's standardInput
task.standardInput = NSPipe ()
let output = NSPipe ()
task.standardOutput = output
task.launch()
task.waitUntilExit()
return output.fileHandleForReading.read().trim()
}
/** Turn a sequence into valid parameters for a shell command, properly quoted */
public func parameters <S : SequenceType> (sequence: S) -> String {
return " " + " ".join( sequence.map { "\"\($0)\"" } )
}
-111
View File
@@ -9,114 +9,3 @@
import Foundation
public typealias FileHandle = NSFileHandle
extension FileHandle: ReadableStreamType {
public func readSome () -> String? {
let data: NSData = self.availableData
if data.length == 0 {
return nil
} else {
if let result = NSString(data: data, encoding: streamencoding) {
return result as String
} else {
printErrorAndExit("Fatal error - could not read stream.")
}
}
}
public func read () -> String {
let data: NSData = self.readDataToEndOfFile()
if let result = NSString(data: data, encoding: streamencoding) {
return result as String
} else {
printErrorAndExit("Fatal error - could not read stream.")
}
}
public func lines () -> AnySequence<String> {
return split("\n")(stream: self)
}
public func writeTo <Target : OutputStreamType> (inout target: Target) {
while let some = self.readSome() {
target.write(some)
}
}
}
extension FileHandle: WriteableStreamType {
public func write (string: String) {
writeData(string.dataUsingEncoding(streamencoding, allowLossyConversion:false)!)
}
public func writeln (string: String) {
self.write(string + "\n")
}
public func writeln () {
self.write("\n")
}
public func closeStream () {
self.closeFile()
}
}
/** Print message to standard error and halt execution. */
@noreturn public func printErrorAndExit <T> (errormessage: T) {
standarderror.writeln("SwiftShell: \(errormessage)")
exit(EXIT_FAILURE)
}
/** Open a file for reading, and exit if an error occurs. */
public func open (path: String) -> ReadableStreamType {
let url = toURLOrError(path)
do {
let filehandle = try FileHandle(forReadingFromURL: url)
return filehandle
} catch {
do {
try url.checkResourceIsReachable()
} catch {
printErrorAndExit(error)
}
printErrorAndExit(error)
}
}
/**
Open a file for writing, create it if it doesn't exist, and exit if an error occurs.
If the file already exists and overwrite=false, the writing will begin at the end of the file.
- parameter overwrite: If true, replace the file if it exists.
*/
public func open (forWriting path: String, overwrite: Bool = false) -> WriteableStreamType {
let url = toURLOrError(path)
if overwrite || !File.fileExistsAtPath(url.path!) {
File.createFileAtPath(url.path!, contents: nil, attributes: nil)
}
do {
let filehandle = try FileHandle(forWritingToURL: url)
filehandle.seekToEndOfFile()
return filehandle
} catch {
do {
try url.checkResourceIsReachable()
} catch {
printErrorAndExit(error)
}
printErrorAndExit(error)
}
}
public let environment = NSProcessInfo.processInfo().environment as [String: String]
public let standardinput = FileHandle.fileHandleWithStandardInput() as ReadableStreamType
public let standardoutput = FileHandle.fileHandleWithStandardOutput() as WriteableStreamType
public let standarderror = FileHandle.fileHandleWithStandardError() as WriteableStreamType
+1 -46
View File
@@ -9,51 +9,6 @@
import Foundation
func toURLOrError (path: String) -> NSURL {
// no longer returns an optional. Weird. And it also seems to confuse the compiler (Swift 2 b2)
let result = NSURL.fileURLWithPath(path)
return result
}
/** The default NSFileManager */
public let File = NSFileManager.defaultManager()
public let Files = NSFileManager.defaultManager()
/**
The tempdirectory is unique each time a script is run and is created the first time it is used.
It lies in the user's temporary directory and will be automatically deleted at some point.
*/
public let tempdirectory: String = {
let tempdirectory = NSTemporaryDirectory() / "SwiftShell-" + NSProcessInfo.processInfo().globallyUniqueString
do {
try File.createDirectoryAtPath(tempdirectory, withIntermediateDirectories:true, attributes: nil)
} catch let error as NSError {
printErrorAndExit("Could not create new temporary directory '\(tempdirectory)':\n\(error.localizedDescription)")
} catch {
printErrorAndExit("Unexpected error: \(error)")
}
return tempdirectory
}()
/**
The current working directory.
Must be used instead of `run("cd ...")` because all the `run` commands are executed in a
separate process and changing the directory there will not affect the rest of the Swift script.
This directory is also used as the base for relative URL's.
*/
public var workdirectory: String {
get { return File.currentDirectoryPath }
set {
if !File.changeCurrentDirectoryPath(newValue) {
printErrorAndExit("Could not change the working directory to \(newValue)")
}
}
}
/** Allows for `"/directory" / "file.extension"` etc. */
public func / (leftpath: String, rightpath: String) -> String {
return toURLOrError(leftpath).URLByAppendingPathComponent(rightpath).path!
}
-107
View File
@@ -1,107 +0,0 @@
/*
* Released under the MIT License (MIT), http://opensource.org/licenses/MIT
*
* Copyright (c) 2014 Kåre Morstøl, NotTooBad Software (nottoobadsoftware.com)
*
* Contributors:
* Kåre Morstøl, https://github.com/kareman - initial API and implementation.
*/
/**
This file contains operators and functions inspired by Functional Programming.
It can be used on its own.
*/
infix operator |> { precedence 50 associativity left }
public func |> <T,U> (lhs: T, rhs: T -> U) -> U {
return rhs(lhs)
}
/** Lazily return a sequence containing the elements of source, in order, that satisfy the predicate includeElement */
public func filter <S : SequenceType>
(includeElement: (S.Generator.Element) -> Bool)
(source: S)
-> LazySequence<FilterSequence<S>> {
return lazy(source).filter(includeElement)
}
/**
Return an `Array` containing the sorted elements of `source` according to 'isOrderedBefore'.
Requires: `isOrderedBefore` is a `strict weak ordering
<http://en.wikipedia.org/wiki/Strict_weak_order#Strict_weak_orderings>` over `elements`.
*/
public func sorted <S : SequenceType>
(isOrderedBefore: (S.Generator.Element, S.Generator.Element) -> Bool)
(source: S)
-> [S.Generator.Element] {
return source.sort(isOrderedBefore)
}
/** Lazily return a sequence containing the results of mapping transform over source. */
public func map <S: SequenceType, T>
(transform: (S.Generator.Element) -> T)
(source: S)
-> LazySequence<MapSequence<S, T>> {
return lazy(source).map(transform)
}
/**
Return the result of repeatedly calling combine with an accumulated value
initialized to initial and each element of sequence, in turn.
*/
public func reduce <S : SequenceType, U>
(initial: U, combine: (U, S.Generator.Element) -> U)
(sequence: S)
-> U {
return sequence.reduce(initial, combine: combine)
}
/** Split text over delimiter, returning an array. */
public func split (delimiter delimiter: String = "\n")(text: String) -> [String] {
return text.componentsSeparatedByString(delimiter)
}
/** Insert separator between each item in elements. */
public func join <C : ExtensibleCollectionType, S : SequenceType where S.Generator.Element == C>
(separator: C)
(elements: S)
-> C {
return join(separator, elements)
}
/** Turn a sequence into an array. For use after the |> operator. */
public func toArray <S : SequenceType> (sequence: S) -> [S.Generator.Element] {
return Array(sequence)
}
/** Discard all elements in `tobedropped` from sequence. */
public func drop <S : SequenceType, T : Equatable where S.Generator.Element == T>
(tobedropped: [T])
(sequence: S)
-> LazySequence<FilterSequence<S>> {
return sequence |> filter { !tobedropped.contains($0) }
}
/** Return at most the first `numbertotake` elements of sequence */
public func take <S : SequenceType, T where S.Generator.Element == T>
(numbertotake: Int)(sequence: S) -> [T] {
var generator = sequence.generate()
var result = [T]()
for _ in 0..<numbertotake {
if let value = generator.next() {
result.append(value)
} else {
break
}
}
return result
}
-179
View File
@@ -9,182 +9,3 @@
import Foundation
// TODO: get encoding from environmental variable LC_CTYPE
public var streamencoding = NSUTF8StringEncoding
/** A stream of text. Does as much as possible lazily. */
public protocol ReadableStreamType : Streamable {
/**
Whatever amount of text the stream feels like providing.
If the source is a file this will read everything at once.
- returns: more text from the stream, or nil if we have reached the end.
*/
func readSome () -> String?
/** Read everything at once. */
func read () -> String
/** Lazily split the stream into lines. */
func lines () -> AnySequence<String>
/** Enable stream to be used by "println" and "toString". */
func writeTo <Target : OutputStreamType> (inout target: Target)
}
/** An output stream, like standard output and standard error. */
public protocol WriteableStreamType : OutputStreamType {
func write (string: String)
func writeln (string: String)
func writeln ()
/** Must be called on local streams when finished writing. */
func closeStream ()
}
/** Create a stream from a String. */
public func stream (text: String) -> ReadableStreamType {
let pipe = NSPipe()
let input = pipe.fileHandleForWriting
input.write(text)
input.closeStream()
return pipe.fileHandleForReading
}
/**
Create a stream from a function returning a generator function, which is called every time the stream is asked for more text.
stream {
// initialisation...
return {
// called repeatedly by the resulting stream
// return text, or return nil when done.
}
}
- returns: The output stream.
*/
public func stream ( closure:() -> () -> String? ) -> ReadableStreamType {
let getmoretext = closure()
let pipe = NSPipe()
let input: NSFileHandle = pipe.fileHandleForWriting
input.writeabilityHandler = { filehandle in
if let text = getmoretext() {
filehandle.write(text)
} else {
// close the stream
input.writeabilityHandler = nil
}
}
return pipe.fileHandleForReading
}
/** Create a stream from a sequence of Strings. */
public func stream <Seq : SequenceType where Seq.Generator.Element == String> (sequence: Seq) -> ReadableStreamType {
return stream {
var generator = sequence.generate()
return { generator.next() }
}
}
/**
Return a writable stream and a readable stream. What you write to the 1st one can be read from the 2nd one.
Make sure to call closeStream() on the writable stream before you call read() on the readable one.
*/
public func streams () -> (WriteableStreamType, ReadableStreamType) {
let pipe = NSPipe()
return (pipe.fileHandleForWriting, pipe.fileHandleForReading)
}
/** Split a stream into parts separated by "delimiter". */
struct StringStreamGenerator : GeneratorType {
private let stream: ReadableStreamType
private let delimiter: String
private var cache = ""
init (stream: ReadableStreamType, delimiter: String = "\n") {
self.stream = stream
self.delimiter = delimiter
}
/** Pass on the stream until the next occurrence of "delimiter" */
mutating func next () -> String? {
let (nextpart, returneddelimiter, remainder) = cache.partition(delimiter)
let delimiterwasfound = returneddelimiter != ""
cache = remainder
if delimiterwasfound {
return nextpart
} else {
if let newcache = stream.readSome() {
// add the next part of the stream to what's left of the previous one,
// and start again from the beginning of the function.
cache = nextpart + newcache
return next()
} else {
// the stream is empty and there are no more delimiters left.
// return whatever is left, or nil if empty.
return nextpart == "" ? nil : nextpart
}
}
}
}
/** Split a stream lazily */
public func split (delimiter: String = "\n")(stream: ReadableStreamType) -> AnySequence<String> {
return AnySequence({StringStreamGenerator (stream: stream, delimiter: delimiter)})
}
/**
Write something to a stream.
something |> writeTo(writablestream)
*/
public func writeTo <T> (stream: WriteableStreamType)(input: T) {
stream.write( String(input) )
}
// needed to avoid `writeTo(SequenceType)` being called instead,
// treating the string as a sequence of characters.
/** Write a String to a writable stream. */
public func writeTo (stream: WriteableStreamType)(input: String) {
stream.write(input)
}
/** Write a sequence to a stream. */
public func writeTo <S : SequenceType> (stream: WriteableStreamType)(seq: S) {
for item in seq {
item |> writeTo(stream)
}
}
infix operator |>> { precedence 50 associativity left }
/**
Write something to a stream.
something |>> writablestream
*/
public func |>> <T> (input: T, stream: WriteableStreamType) {
writeTo(stream)(input: input)
}
// needed to avoid `func |>> <S : SequenceType>` being called instead,
// treating the string as a sequence of characters.
/** Write a String to a writable stream. */
public func |>> (text: String, stream: WriteableStreamType) {
writeTo(stream)(input: text)
}
/** Write a sequence to a stream. */
public func |>> <S : SequenceType> (seq: S, stream: WriteableStreamType) {
writeTo(stream)(seq: seq)
}
-58
View File
@@ -9,61 +9,3 @@
import Foundation
extension String {
public func replace (oldString: String, _ newString: String) -> String {
return self.stringByReplacingOccurrencesOfString(oldString, withString: newString)
}
/** Replace the first `limit` occurrences of oldString with newString. */
public func replace (oldString: String, _ newString: String, limit: Int) -> String {
let ranges = self.findAll(oldString) |> take(limit)
return ranges.count == 0
? self
: self.stringByReplacingOccurrencesOfString(oldString, withString: newString,
range: ranges.first!.startIndex ..< ranges.last!.endIndex)
}
public func split (sep: String) -> [String] {
return self.componentsSeparatedByString(sep)
}
public func trim () -> String {
return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
}
public func countOccurrencesOf (substring: String) -> Int {
return (self.findAll(substring) |> toArray).count
}
/** A lazy sequence of the ranges of `findstring` in this string. */
public func findAll (findstring: String) -> AnySequence<Range<String.Index>> {
var rangeofremainder: Range = self.startIndex..<self.endIndex
return AnySequence (anyGenerator {
if let foundrange = self.rangeOfString(findstring, range:rangeofremainder) {
rangeofremainder = foundrange.endIndex..<self.endIndex
return foundrange
} else {
return nil
}
})
}
/**
Split the string at the first occurrence of separator, and return a 3-tuple containing the part
before the separator, the separator itself, and the part after the separator. If the separator is
not found, return a 3-tuple containing the string itself, followed by two empty strings.
*/
public func partition (separator: String) -> (String, String, String) {
if let separatorRange = self.rangeOfString(separator) {
if !separatorRange.isEmpty {
let firstpart = self[self.startIndex ..< separatorRange.startIndex]
let secondpart = self[separatorRange.endIndex ..< self.endIndex]
return (firstpart, separator, secondpart)
}
}
return (self,"","")
}
}
-4
View File
@@ -11,7 +11,6 @@
BA2E3B331B5C35CC001ED175 /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2E3B2D1B5C35CC001ED175 /* Command.swift */; };
BA2E3B341B5C35CC001ED175 /* FileHandle.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2E3B2E1B5C35CC001ED175 /* FileHandle.swift */; };
BA2E3B351B5C35CC001ED175 /* Files.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2E3B2F1B5C35CC001ED175 /* Files.swift */; };
BA2E3B361B5C35CC001ED175 /* Pipes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2E3B301B5C35CC001ED175 /* Pipes.swift */; };
BA2E3B371B5C35CC001ED175 /* Stream.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2E3B311B5C35CC001ED175 /* Stream.swift */; };
BA2E3B381B5C35CC001ED175 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2E3B321B5C35CC001ED175 /* String.swift */; };
BA2E3B431B5C3635001ED175 /* Command_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA2E3B391B5C3635001ED175 /* Command_Tests.swift */; };
@@ -44,7 +43,6 @@
BA2E3B2D1B5C35CC001ED175 /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
BA2E3B2E1B5C35CC001ED175 /* FileHandle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileHandle.swift; sourceTree = "<group>"; };
BA2E3B2F1B5C35CC001ED175 /* Files.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Files.swift; sourceTree = "<group>"; };
BA2E3B301B5C35CC001ED175 /* Pipes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Pipes.swift; sourceTree = "<group>"; };
BA2E3B311B5C35CC001ED175 /* Stream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stream.swift; sourceTree = "<group>"; };
BA2E3B321B5C35CC001ED175 /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
BA2E3B391B5C3635001ED175 /* Command_Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command_Tests.swift; sourceTree = "<group>"; };
@@ -104,7 +102,6 @@
BA2E3B2D1B5C35CC001ED175 /* Command.swift */,
BA2E3B2E1B5C35CC001ED175 /* FileHandle.swift */,
BA2E3B2F1B5C35CC001ED175 /* Files.swift */,
BA2E3B301B5C35CC001ED175 /* Pipes.swift */,
BA2E3B311B5C35CC001ED175 /* Stream.swift */,
BA2E3B321B5C35CC001ED175 /* String.swift */,
BA2E3B1A1B5C34DD001ED175 /* Info.plist */,
@@ -242,7 +239,6 @@
BA2E3B331B5C35CC001ED175 /* Command.swift in Sources */,
BA2E3B341B5C35CC001ED175 /* FileHandle.swift in Sources */,
BA2E3B351B5C35CC001ED175 /* Files.swift in Sources */,
BA2E3B361B5C35CC001ED175 /* Pipes.swift in Sources */,
BA2E3B371B5C35CC001ED175 /* Stream.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
-35
View File
@@ -11,39 +11,4 @@ import XCTest
class Command_Tests: XCTestCase {
func testSimpleCommand () {
SwiftShell.run("echo")
}
func testSimpleCommandWithOutput () {
XCTAssertEqual( SwiftShell.run("echo this is streamed").read(), "this is streamed\n" )
}
func testChainedCommands () {
let result = SwiftShell.run("echo this is streamed") |> SwiftShell.run("wc -w")
XCTAssertEqual( result.read().trim(), "3", "the number of words should be 3" )
}
func testInlineCommand () {
XCTAssertEqual( $("echo one"), "one" )
}
func testInlineCommandInsideRunCommandAfterPipe () {
let result = stream("line 1\nline 2\nline 3") |> SwiftShell.run("grep " + $("echo 2"))
XCTAssertEqual( result.read(), "line 2\n" )
}
func testParametersFromSequenceOfStrings () {
XCTAssertEqual( parameters(["one", "two", "three"]), " \"one\" \"two\" \"three\"" )
}
func testParametersFromSequenceOfStreams () {
let result = AnySequence( [stream("one"), stream("two"), stream("three")].generate() ) |> parameters
XCTAssertEqual( result, " \"one\" \"two\" \"three\"" )
}
func testParametersFromSequenceOfNumbers () {
XCTAssertEqual( parameters([1, 2, 3]), " \"1\" \"2\" \"3\"" )
}
}
-55
View File
@@ -11,60 +11,5 @@ import XCTest
class FileHandle_Tests: XCTestCase {
func notestOpenForReadingFileWhichDoesNotExist () {
// prints error message and stops execution.
open("file which does not exist")
}
func testReadFileLineByLine () {
let shorttextpath = pathForTestResource("shorttext", type: "txt")
var contents = ""
for line in open(shorttextpath).lines() {
contents.write(line)
}
XCTAssertFalse(contents.isEmpty, "could not read from file")
}
func testOpenForWritingFileWhichDoesNotExist () {
let path = tempdirectory + "/testOpenForWritingFileWhichDoesNotExist.txt"
let file = open(forWriting: path)
file.writeln( "line 1")
file.closeStream()
XCTAssertEqual( open(path).read(), "line 1\n" )
}
func testOpenForOverWritingFileWhichDoesNotExist () {
let path = tempdirectory + "/testOpenForOverWritingFileWhichDoesNotExist.txt"
let file = open(forWriting: path, overwrite: true)
file.writeln( "line 1")
file.closeStream()
XCTAssertEqual( open(path).read(), "line 1\n" )
}
func testOpenForWritingExistingFile_AppendsFile () {
let path = tempdirectory + "/testOpenForWritingExistingFile_AppendsFile.txt"
SwiftShell.run("echo existing line > " + path)
let file = open(forWriting: path)
file.writeln( "new line")
file.closeStream()
XCTAssertEqual( open(path).read(), "existing line\nnew line\n" )
}
func testOpenForOverWritingExistingFile () {
let path = tempdirectory + "/testOpenForOverWritingExistingFile.txt"
SwiftShell.run("echo existing line > " + path)
let file = open(forWriting: path, overwrite: true)
file.writeln( "new line")
file.closeStream()
XCTAssertEqual( open(path).read(), "new line\n" )
}
}
-20
View File
@@ -11,24 +11,4 @@ import XCTest
class Files_Tests: XCTestCase {
func testTempDirectory_IsTheSameAfterRepeatedCalls () {
XCTAssertEqual( tempdirectory, tempdirectory )
}
func testWorkDirectory_IsCurrentDirectory () {
XCTAssertEqual( workdirectory, NSFileManager.defaultManager().currentDirectoryPath )
}
func testWorkDirectory_CanChange () {
workdirectory = "/tmp"
XCTAssertEqual( workdirectory, "/private/tmp" )
XCTAssertEqual( $("pwd"), "/tmp" )
}
func testURLConcatenationOperator () {
XCTAssertEqual( "/directory" / "file.extension", "/directory/file.extension" )
XCTAssertEqual( "/root" / "directory" / "file.extension", "/root/directory/file.extension" )
XCTAssertEqual( "directory" / "file.extension", workdirectory + "/directory/file.extension" )
}
}
-42
View File
@@ -11,47 +11,5 @@ import XCTest
class Pipes_Tests: XCTestCase {
/*
func testSplitMapFilterSortedAndJoinWithPipeForwardOperator () {
let numbers = "4,1,6,2,5,3,9,7,0,8"
// doing this on one line takes a very very long time to compile (6.1 beta 2)
let numberslessthan5 = numbers |> split(delimiter: ",") |> map {Int($0)!} |> filter {$0<5}
let result = numberslessthan5 |> sorted {$0>$1} |> map {String($0)!} |> join(",")
XCTAssertEqual( result, "4,3,2,1,0" )
}
*/
func testSplitAndReduceWithPipeForwardOperator () {
let numbers = "4,1,6,2,5,3,9,7,0,8"
let result = numbers |> split(delimiter: ",") |> reduce(0) {$0 + Int($1)!}
XCTAssertEqual( result, 45 )
}
func testTurnSequenceIntoArray () {
let numbers = [4,1,6]
let result = AnySequence(numbers) |> toArray
XCTAssertEqual( result, [4,1,6] )
}
func testDrop () {
let numbers = [1,2,3,4,5,6]
let result = numbers |> drop([2,3,5]) |> toArray
XCTAssertEqual( result, [1,4,6] )
}
func testTake () {
let numbers = [1,2,3,4,5,6]
XCTAssertEqual( numbers |> take(3), [1,2,3] )
XCTAssertEqual( numbers |> take(100), [1,2,3,4,5,6] )
XCTAssertEqual( numbers |> take(0), [] )
}
}
+1 -25
View File
@@ -11,30 +11,6 @@ import SwiftShell
class Stream_Iteration_Tests: XCTestCase {
func testIterateOverFileHandle () {
var filehandletest = ""
for line in stream("line 1\nline 2\n").lines() {
filehandletest += line + "\n"
}
XCTAssertEqual(filehandletest , "line 1\nline 2\n")
XCTAssertEqual(["line 1","line 2"] , Array(stream("line 1\nline 2").lines()))
XCTAssertEqual(["line 1"] , Array(stream("line 1\n").lines()))
XCTAssertEqual(["line 1"] , Array(stream("line 1").lines()))
XCTAssertEqual(["line 1", "", "line 3"] , Array(stream("line 1\n\nline 3").lines()))
XCTAssertEqual(["", "line 2", "line 3"] , Array(stream("\nline 2\nline 3").lines()))
XCTAssertEqual(["", "", "line 3"] , Array(stream("\n\nline 3").lines()))
}
func testIterateOverStreamInPieces () {
XCTAssertEqual(["line 1","line 2"] , Array(stream(["line"," 1\nline 2"]).lines()))
XCTAssertEqual(["line 1"] , Array(stream(["line 1","\n"]).lines()))
XCTAssertEqual(["line 1"] , Array(stream(["li","ne"," 1"]).lines()))
XCTAssertEqual(["line 1", "", "line 3"] , Array(stream(["line 1\n","\n","line 3"]).lines()))
XCTAssertEqual(["line 1", "", "line 3"] , Array(stream(["line 1\n","\nline 3"]).lines()))
XCTAssertEqual(["", "line 2", "line 3"] , Array(stream(["\nline 2\n","line 3"]).lines()))
XCTAssertEqual(["", "", "line 3"] , Array(stream(["\n","\nli","ne 3"]).lines()))
}
}
-85
View File
@@ -10,90 +10,5 @@ import SwiftShell
import XCTest
class Stream_Tests: XCTestCase {
func testStreamFromAString () {
XCTAssertEqual( "this is a string", stream("this is a string").read() )
XCTAssertEqual( "These are weird↔️🐻♨︎", stream("These are weird↔️🐻♨︎").read() )
}
func testCustomStream () {
let result = stream ({
var finished = false
return {
if !finished {
finished = true
return "this is it"
} else {
return nil
}
}
})
XCTAssertEqual( result.read(), "this is it" )
}
func testStreamFromArray () {
XCTAssertEqual( stream(["item 1","item 2"]).read(), "item 1item 2" )
}
func testPrintStreamToStream () {
let (writable, readable) = streams()
stream("this goes in") |>> writable
XCTAssertEqual( readable.readSome()!, "this goes in" )
}
func testPrintStreamToStreamInPieces () {
let (writable, readable) = streams()
stream(["this ", "goes", " in"]) |>> writable
writable.closeStream()
XCTAssertEqual( readable.read(), "this goes in" )
}
func testPrintStringToStream () {
let (writable, readable) = streams()
"this goes in" |>> writable
XCTAssertEqual( readable.readSome()!, "this goes in" )
}
func testCommandChainToStream () {
let (writable, readable) = streams()
SwiftShell.run("echo this is streamed") |> SwiftShell.run("wc -w") |>> writable
XCTAssertEqual( readable.readSome()!.trim(), "3" )
}
func testSequenceOfStreamsToStream () {
let (writable, readable) = streams()
// make sure the array isn't printed as a Printable.
AnySequence([stream("line 1"), stream("line 2"), stream("line 3")].generate()) |>> writable
XCTAssertEqual( readable.readSome()!.trim(), "line 1line 2line 3" )
}
func testChainWithSequenceOfStreamsPrintedToStream () {
let (writable, readable) = streams()
let dict = ["test":"line 1:line 2:line 3"]
dict["test"]! |> split(delimiter: ":")
|> map { line in SwiftShell.run("echo \(line)") }
|>> writable
XCTAssertEqual( readable.readSome()!.trim(), "line 1\nline 2\nline 3" )
}
func testChainWithSequenceOfStringsPrintedToStream () {
let (writable, readable) = streams()
var i = 1
stream("line 1\nline 2\nline 3").lines() |> map {line in "line \(i++): \(line)\n"} |>> writable
XCTAssertEqual( readable.readSome()!.trim(), "line 1: line 1\nline 2: line 2\nline 3: line 3" )
}
}
+1 -47
View File
@@ -11,52 +11,6 @@ import SwiftShell
class String_Partition_Tests: XCTestCase {
func testPartitionWithSeparatorInTheMiddle () {
let text = "the first part\nthe second part"
let (firstpart, returnedseparator, secondpart) = text.partition ("\n")
XCTAssertEqual( firstpart, "the first part")
XCTAssertEqual( returnedseparator, "\n")
XCTAssertEqual( secondpart, "the second part")
}
func testPartitionWithNoSeparatorFound () {
let text = "the first partthe second part"
let (firstpart, returnedseparator, secondpart) = text.partition ("\n")
XCTAssertEqual( firstpart, "the first partthe second part")
XCTAssertEqual( returnedseparator, "")
XCTAssertEqual( secondpart, "")
}
func testPartitionWithSeparatorAtTheEnd () {
let text = "the first partthe second part\n"
let (firstpart, returnedseparator, secondpart) = text.partition ("\n")
XCTAssertEqual( firstpart, "the first partthe second part")
XCTAssertEqual( returnedseparator, "\n")
XCTAssertEqual( secondpart, "")
}
func testPartitionWithSeparatorAtTheBeginning () {
let text = "\nthe first partthe second part\n"
let (firstpart, returnedseparator, secondpart) = text.partition ("\n")
XCTAssertEqual( firstpart, "")
XCTAssertEqual( returnedseparator, "\n")
XCTAssertEqual( secondpart, "the first partthe second part\n")
}
func testPartitionWithSeveralSeparators () {
let text = "the first partthe second part"
let (firstpart, returnedseparator, secondpart) = text.partition ("part")
XCTAssertEqual( firstpart, "the first ")
XCTAssertEqual( returnedseparator, "part")
XCTAssertEqual( secondpart, "the second part")
}
func testPartitionWithEmptyText () {
let text = ""
let (firstpart, returnedseparator, secondpart) = text.partition ("part")
XCTAssertEqual( firstpart, "")
XCTAssertEqual( returnedseparator, "")
XCTAssertEqual( secondpart, "")
}
}
-49
View File
@@ -11,53 +11,4 @@ import SwiftShell
class String_Tests: XCTestCase {
func testCountOccurrencesOfStringInstring () {
let text = "how many are there here"
XCTAssertEqual( text.countOccurrencesOf( "e"), 5 )
XCTAssertEqual( text.countOccurrencesOf( "x"), 0 )
XCTAssertEqual( text.countOccurrencesOf( "h"), 3 )
XCTAssertEqual( text.countOccurrencesOf( "ho"), 1 )
XCTAssertEqual( text.countOccurrencesOf( "re"), 3 )
XCTAssertEqual( text.countOccurrencesOf( "Not found"), 0 )
}
func testFindAll () {
func ranges(findstring: String) -> [Range<String.Index>] {
let text = "a b c aa bbb cc ab bc ca"
return text.findAll(findstring) |> toArray
}
XCTAssertEqual( ranges("a").count, 5 )
XCTAssertEqual( ranges("bb").count, 1 )
XCTAssertEqual( ranges("a ").count, 2 )
}
func testReplaceStringwithstring () {
let text = "a b c aa bb cc ab bc ca"
XCTAssertEqual( text.replace("a", "x"), "x b c xx bb cc xb bc cx" )
XCTAssertEqual( text.replace("b ", "x"), "a xc aa bxcc axbc ca" )
}
func testReplaceOnlySomeStringsWithString () {
let text = "a b c aa bb cc ab bc ca"
XCTAssertEqual( text.replace("a", "x", limit: 0), "a b c aa bb cc ab bc ca" )
XCTAssertEqual( text.replace("a", "x", limit: 2), "x b c xa bb cc ab bc ca" )
XCTAssertEqual( text.replace("a", "x", limit: 4), "x b c xx bb cc xb bc ca" )
XCTAssertEqual( text.replace("a", "x", limit: 5), "x b c xx bb cc xb bc cx" )
XCTAssertEqual( text.replace("a", "x", limit: 6), "x b c xx bb cc xb bc cx" )
XCTAssertEqual( text.replace("a", "[xy]", limit: 4), "[xy] b c [xy][xy] bb cc [xy]b bc ca" )
XCTAssertEqual( text.replace("a", "[xy]", limit: 5), "[xy] b c [xy][xy] bb cc [xy]b bc c[xy]" )
XCTAssertEqual( text.replace("a", "[xy]", limit: 6), "[xy] b c [xy][xy] bb cc [xy]b bc c[xy]" )
XCTAssertEqual( text.replace("a ", "x", limit: 0), "a b c aa bb cc ab bc ca" )
XCTAssertEqual( text.replace("a ", "x", limit: 1), "xb c aa bb cc ab bc ca" )
XCTAssertEqual( text.replace("a ", "x", limit: 2), "xb c axbb cc ab bc ca" )
XCTAssertEqual( text.replace("a ", "x", limit: 3), "xb c axbb cc ab bc ca" )
}
}