-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Set up the repository with the Truth, StringUtils and OptionalUtils m…
…odules. (#3)
- Loading branch information
Showing
71 changed files
with
3,113 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
4.1.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# Ignore Bazel symlinks | ||
bazel-* | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") | ||
|
||
swift_library( | ||
name = "OptionalUtils", | ||
srcs = glob(["**/*.swift"]), | ||
module_name = "OptionalUtils", | ||
visibility = ["//visibility:public"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
public extension Optional { | ||
/// Unwraps the optional | ||
func isNotNil(file: StaticString = #file, line: UInt = #line) throws -> Wrapped { | ||
guard let wrapped = self else { | ||
throw OptionalError.unexpectedNil(file, line) | ||
} | ||
return wrapped | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
enum OptionalError: Error { | ||
/// First arg is the source filename, second arg is the line number | ||
case unexpectedNil(StaticString, UInt) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Loving inspired by | ||
// https://stackoverflow.com/questions/27989094/how-to-unwrap-an-optional-value-from-any-type/32516815. | ||
|
||
public protocol OptionalProtocol { | ||
associatedtype Wrapped | ||
|
||
func isSome() -> Bool | ||
func isNone() -> Bool | ||
func wrappedValue() throws -> Wrapped | ||
} | ||
|
||
extension Optional: OptionalProtocol { | ||
enum OptionalProtocolError: Error { | ||
case noValue | ||
} | ||
|
||
public func isSome() -> Bool { | ||
switch self { | ||
case .none: return false | ||
case .some: return true | ||
} | ||
} | ||
|
||
public func isNone() -> Bool { | ||
return !isSome() | ||
} | ||
|
||
public func wrappedValue() throws -> Wrapped { | ||
switch self { | ||
case .none: | ||
throw OptionalProtocolError.noValue | ||
case let .some(wrappedValue): | ||
return wrappedValue | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") | ||
|
||
swift_library( | ||
name = "StringUtils", | ||
srcs = glob(["**/*.swift"]), | ||
module_name = "StringUtils", | ||
visibility = ["//visibility:public"], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import Foundation | ||
|
||
enum DataStringConversionError: Error { | ||
case failedToConvertDataToString(String.Encoding) | ||
case failedToConvertStringToData(String.Encoding) | ||
} | ||
|
||
extension Data { | ||
public func toString(using encoding: String.Encoding = .utf8) throws -> String { | ||
guard let result = String(data: self, encoding: encoding) else { | ||
throw DataStringConversionError.failedToConvertDataToString(encoding) | ||
} | ||
return result | ||
} | ||
} | ||
|
||
extension String { | ||
public func toData(using encoding: String.Encoding = .utf8) throws -> Data { | ||
guard let result = data(using: encoding) else { | ||
throw DataStringConversionError.failedToConvertStringToData(encoding) | ||
} | ||
return result | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
extension StaticString { | ||
public func toString() -> String { | ||
withUTF8Buffer { String(decoding: $0, as: UTF8.self) } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Adapted from https://stackoverflow.com/questions/39677330/how-does-string-substring-work-in-swift | ||
// Note: Using integers is not perfect. However, it makes some manipulations much easier to grok. | ||
|
||
public extension String { | ||
func index(from offset: Int) -> Index { | ||
return index(startIndex, offsetBy: offset) | ||
} | ||
|
||
func substring(from idx: Int) -> Substring { | ||
let fromIndex = index(from: idx) | ||
return self[fromIndex...] | ||
} | ||
|
||
func substring(to idx: Int) -> Substring { | ||
let toIndex = index(from: idx) | ||
return self[..<toIndex] | ||
} | ||
|
||
func substring(with range: Range<Int>) -> Substring { | ||
let startIndex = index(from: range.lowerBound) | ||
let endIndex = index(from: range.upperBound) | ||
return self[startIndex ..< endIndex] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
extension String { | ||
// Lovingly inspired by https://stackoverflow.com/questions/26845307/generate-random-alphanumeric-string-in-swift | ||
|
||
/// Specifies the type of random string that should be generated. | ||
public enum RandomStringMode { | ||
case upperAlpha | ||
case lowerAlpha | ||
case upperAlphaNumeric | ||
case lowerAlphaNumeric | ||
case alpha | ||
case alphaNumeric | ||
|
||
/// Returns the characters that should be used for the mode. | ||
public var letters: String { | ||
switch self { | ||
case .upperAlpha: | ||
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
case .lowerAlpha: | ||
return "abcdefghijklmnopqrstuvwxyz" | ||
case .upperAlphaNumeric: | ||
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" | ||
case .lowerAlphaNumeric: | ||
return "abcdefghijklmnopqrstuvwxyz0123456789" | ||
case .alpha: | ||
return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" | ||
case .alphaNumeric: | ||
return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" | ||
} | ||
} | ||
} | ||
|
||
/// Generates a random string with the specified length and the specified contents. | ||
public static func random(length: Int, mode: RandomStringMode) -> String { | ||
return String((0 ..< length).map { _ in mode.letters.randomElement()! }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import XCTest | ||
|
||
/// Return a subject for the target. | ||
public func assertThat<T>( | ||
_ actual: T, | ||
using failureStrategy: FailureStrategy, | ||
with customMessage: MessageClosure? = nil | ||
) -> Subject<T> { | ||
var customMessages = [MessageClosure]() | ||
if let customMessage = customMessage { | ||
customMessages.append(customMessage) | ||
} | ||
return Subject(actual: actual, failureStrategy: failureStrategy, customMessages: customMessages) | ||
} | ||
|
||
// Create a subject for the target and pass it to the provided consumer. This form is useful when | ||
// wanting to perform a number of assertions on a single subject. This avoids massive compilation | ||
// times when chaining assertions. | ||
@discardableResult | ||
public func assertThat<T>( | ||
_ actual: T, | ||
using failureStrategy: FailureStrategy, | ||
with customMessage: MessageClosure? = nil, | ||
_ consumer: (Subject<T>) -> Void | ||
) -> Subject<T> { | ||
let subject = assertThat(actual, using: failureStrategy, with: customMessage) | ||
consumer(subject) | ||
return subject | ||
} | ||
|
||
/// Return a subject for the throwable expression. | ||
public func assertThat<T>( | ||
using failureStrategy: FailureStrategy, | ||
_ expression: @escaping () throws -> T | ||
) -> ThrowsSubject<T> { | ||
return ThrowsSubject(failureStrategy: failureStrategy, expression: expression) | ||
} | ||
|
||
extension XCTestCase { | ||
/// Return a subject for the target. | ||
public func assertThat<T>(_ actual: T, with customMessage: MessageClosure? = nil) -> Subject<T> { | ||
return Truth.assertThat(actual, using: failureStrategy, with: customMessage) | ||
} | ||
|
||
// Create a subject for the target and pass it to the provided consumer. This form is useful when | ||
// wanting to perform a number of assertions on a single subject. This avoids massive compilation | ||
// times when chaining assertions. | ||
@discardableResult | ||
public func assertThat<T>( | ||
_ actual: T, | ||
with customMessage: MessageClosure? = nil, | ||
_ consumer: (Subject<T>) -> Void | ||
) -> Subject<T> { | ||
return Truth.assertThat(actual, using: failureStrategy, with: customMessage, consumer) | ||
} | ||
|
||
/// Return a subject for the throwable expression. | ||
public func assertThat<T>( | ||
expression: @escaping () throws -> T | ||
) -> ThrowsSubject<T> { | ||
return Truth.assertThat(using: failureStrategy, expression) | ||
} | ||
} | ||
|
||
// MARK: - Helpers for Child Subjects | ||
|
||
extension BaseSubject { | ||
/// Returns a Subject for the provided value using this instance's failure strategy. | ||
public func that<T>(_ actual: T) -> Subject<T> { | ||
return Subject( | ||
actual: actual, | ||
failureStrategy: failureStrategy, | ||
customMessages: customMessages, | ||
continueAssertions: continueAssertions | ||
) | ||
} | ||
|
||
/// Returns a Subject for the provided expression using this instance's failure strategy. | ||
public func that<T>( | ||
expression: @escaping ThrowsSubject<T>.ThrowableFn | ||
) -> ThrowsSubject<T> { | ||
return ThrowsSubject( | ||
failureStrategy: failureStrategy, | ||
continueAssertions: continueAssertions, | ||
expression: expression | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") | ||
|
||
swift_library( | ||
name = "Truth", | ||
testonly = 1, | ||
srcs = glob(["**/*.swift"]), | ||
module_name = "Truth", | ||
visibility = ["//visibility:public"], | ||
deps = [ | ||
"//Sources/OptionalUtils", | ||
"//Sources/StringUtils", | ||
], | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
public extension Subject where T: BidirectionalCollection { | ||
/// Provides a means for executing assertions on the last element of the array. This method will | ||
/// return the collection subject for additional assertions. | ||
@discardableResult func lastItem<E>( | ||
file: StaticString = #file, | ||
line: UInt = #line, | ||
_ elemSubjectConsumer: (Subject<E>) throws -> Void | ||
) -> Self where E == T.Element { | ||
guard continueAssertions else { | ||
return self | ||
} | ||
guard let element = actual.last else { | ||
fail( | ||
file: file, | ||
line: line, | ||
Fact("expected the last element to exist") | ||
) | ||
return self | ||
} | ||
do { | ||
let elemSubject = that(element) | ||
try elemSubjectConsumer(elemSubject) | ||
} catch { | ||
fail(file: file, line: line, .unexpectedError(error)) | ||
} | ||
return self | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import Foundation | ||
|
||
extension Subject where T: BinaryInteger { | ||
/// Asserts that the actual value is less than the expected value. | ||
@discardableResult public func isLessThan( | ||
_ expected: T, | ||
file: StaticString = #file, | ||
line: UInt = #line | ||
) -> Self { | ||
return doAssert( | ||
failWith: Failure( | ||
file: file, | ||
line: line, | ||
Fact("expected to be less than", expected), | ||
Fact("but was", actual) | ||
) | ||
) { | ||
return actual < expected | ||
} | ||
} | ||
|
||
/// Asserts that the actual value is greater than the expected value. | ||
@discardableResult public func isGreaterThan( | ||
_ expected: T, | ||
file: StaticString = #file, | ||
line: UInt = #line | ||
) -> Self { | ||
return doAssert( | ||
failWith: Failure( | ||
file: file, | ||
line: line, | ||
Fact("expected to be greater than", expected), | ||
Fact("but was", actual) | ||
) | ||
) { | ||
return actual > expected | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
extension Subject where T == Bool { | ||
/// Asserts that the actual value is true. | ||
public func isTrue(file: StaticString = #file, line: UInt = #line) { | ||
doAssert(failWith: Failure(file: file, line: line, Fact("expected to be true"))) { | ||
actual | ||
} | ||
} | ||
|
||
/// Asserts that the actual value is false. | ||
public func isFalse(file: StaticString = #file, line: UInt = #line) { | ||
doAssert(failWith: Failure(file: file, line: line, Fact("expected to be false"))) { | ||
!actual | ||
} | ||
} | ||
} |
Oops, something went wrong.