Add PromiseKit dependency

- Added PromiseKit dependency
This commit is contained in:
2018-11-15 22:08:00 -04:00
parent 2689d86c18
commit be7b6b5881
541 changed files with 46282 additions and 0 deletions

View File

@@ -0,0 +1,52 @@
import PromiseKit
import XCTest
class AfterTests: XCTestCase {
func testZero() {
let ex2 = expectation(description: "")
after(seconds: 0).done(ex2.fulfill)
waitForExpectations(timeout: 2, handler: nil)
let ex3 = expectation(description: "")
after(.seconds(0)).done(ex3.fulfill)
waitForExpectations(timeout: 2, handler: nil)
#if !SWIFT_PACKAGE
let ex4 = expectation(description: "")
__PMKAfter(0).done{ _ in ex4.fulfill() }.silenceWarning()
waitForExpectations(timeout: 2, handler: nil)
#endif
}
func testNegative() {
let ex2 = expectation(description: "")
after(seconds: -1).done(ex2.fulfill)
waitForExpectations(timeout: 2, handler: nil)
let ex3 = expectation(description: "")
after(.seconds(-1)).done(ex3.fulfill)
waitForExpectations(timeout: 2, handler: nil)
#if !SWIFT_PACKAGE
let ex4 = expectation(description: "")
__PMKAfter(-1).done{ _ in ex4.fulfill() }.silenceWarning()
waitForExpectations(timeout: 2, handler: nil)
#endif
}
func testPositive() {
let ex2 = expectation(description: "")
after(seconds: 1).done(ex2.fulfill)
waitForExpectations(timeout: 2, handler: nil)
let ex3 = expectation(description: "")
after(.seconds(1)).done(ex3.fulfill)
waitForExpectations(timeout: 2, handler: nil)
#if !SWIFT_PACKAGE
let ex4 = expectation(description: "")
__PMKAfter(1).done{ _ in ex4.fulfill() }.silenceWarning()
waitForExpectations(timeout: 2, handler: nil)
#endif
}
}

View File

@@ -0,0 +1,130 @@
import Foundation
import PromiseKit
import XCTest
class CancellationTests: XCTestCase {
func testCancellation() {
let ex1 = expectation(description: "")
let p = after(seconds: 0).done { _ in
throw LocalError.cancel
}.done {
XCTFail()
}
p.catch { _ in
XCTFail()
}
p.catch(policy: .allErrors) {
XCTAssertTrue($0.isCancelled)
ex1.fulfill()
}
waitForExpectations(timeout: 60)
}
func testThrowCancellableErrorThatIsNotCancelled() {
let expct = expectation(description: "")
after(seconds: 0).done { _ in
throw LocalError.notCancel
}.done {
XCTFail()
}.catch {
XCTAssertFalse($0.isCancelled)
expct.fulfill()
}
waitForExpectations(timeout: 1)
}
func testRecoverWithCancellation() {
let ex1 = expectation(description: "")
let ex2 = expectation(description: "")
let p = after(seconds: 0).done { _ in
throw CocoaError.cancelled
}.recover(policy: .allErrors) { err -> Promise<Void> in
ex1.fulfill()
XCTAssertTrue(err.isCancelled)
throw err
}.done { _ in
XCTFail()
}
p.catch { _ in
XCTFail()
}
p.catch(policy: .allErrors) {
XCTAssertTrue($0.isCancelled)
ex2.fulfill()
}
waitForExpectations(timeout: 1)
}
func testFoundationBridging1() {
let ex = expectation(description: "")
let p = after(seconds: 0).done { _ in
throw CocoaError.cancelled
}
p.catch { _ in
XCTFail()
}
p.catch(policy: .allErrors) {
XCTAssertTrue($0.isCancelled)
ex.fulfill()
}
waitForExpectations(timeout: 1)
}
func testFoundationBridging2() {
let ex = expectation(description: "")
let p = Promise().done {
throw URLError.cancelled
}
p.catch { _ in
XCTFail()
}
p.catch(policy: .allErrors) {
XCTAssertTrue($0.isCancelled)
ex.fulfill()
}
waitForExpectations(timeout: 1)
}
#if swift(>=3.2)
func testIsCancelled() {
XCTAssertTrue(PMKError.cancelled.isCancelled)
XCTAssertTrue(URLError.cancelled.isCancelled)
XCTAssertTrue(CocoaError.cancelled.isCancelled)
XCTAssertFalse(CocoaError(_nsError: NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.coderInvalidValue.rawValue)).isCancelled)
}
#endif
}
private enum LocalError: CancellableError {
case notCancel
case cancel
var isCancelled: Bool {
switch self {
case .notCancel: return false
case .cancel: return true
}
}
}
private extension URLError {
static var cancelled: URLError {
return .init(_nsError: NSError(domain: NSURLErrorDomain, code: URLError.Code.cancelled.rawValue))
}
}
private extension CocoaError {
static var cancelled: CocoaError {
return .init(_nsError: NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.userCancelled.rawValue))
}
}

View File

@@ -0,0 +1,267 @@
import PromiseKit
import Dispatch
import XCTest
class CatchableTests: XCTestCase {
func testFinally() {
let finallyQueue = DispatchQueue(label: "\(#file):\(#line)", attributes: .concurrent)
func helper(error: Error, on queue: DispatchQueue = .main, flags: DispatchWorkItemFlags? = nil) {
let ex = (expectation(description: ""), expectation(description: ""))
var x = 0
Promise<Void>(error: error).catch(policy: .allErrors) { _ in
XCTAssertEqual(x, 0)
x += 1
ex.0.fulfill()
}.finally(on: queue, flags: flags) {
if let flags = flags, flags.contains(.barrier) {
dispatchPrecondition(condition: .onQueueAsBarrier(queue))
} else {
dispatchPrecondition(condition: .onQueue(queue))
}
XCTAssertEqual(x, 1)
x += 1
ex.1.fulfill()
}
wait(for: [ex.0, ex.1], timeout: 10)
}
helper(error: Error.dummy)
helper(error: Error.cancelled)
helper(error: Error.dummy, on: finallyQueue)
helper(error: Error.dummy, on: finallyQueue, flags: .barrier)
}
func testCauterize() {
let ex = expectation(description: "")
let p = Promise<Void>(error: Error.dummy)
// cannot test specifically that this outputs to console,
// but code-coverage will note that the line is run
p.cauterize()
p.catch { _ in
ex.fulfill()
}
wait(for: [ex], timeout: 1)
}
}
/// `Promise<Void>.recover`
extension CatchableTests {
func test__void_specialized_full_recover() {
func helper(error: Swift.Error) {
let ex = expectation(description: "")
Promise<Void>(error: error).recover { _ in }.done(ex.fulfill)
wait(for: [ex], timeout: 10)
}
helper(error: Error.dummy)
helper(error: Error.cancelled)
}
func test__void_specialized_full_recover__fulfilled_path() {
let ex = expectation(description: "")
Promise().recover { _ in XCTFail() }.done(ex.fulfill)
wait(for: [ex], timeout: 10)
}
func test__void_specialized_conditional_recover() {
func helper(policy: CatchPolicy, error: Swift.Error, line: UInt = #line) {
let ex = expectation(description: "")
var x = 0
Promise<Void>(error: error).recover(policy: policy) { err in
guard x < 1 else { throw err }
x += 1
}.done(ex.fulfill).silenceWarning()
wait(for: [ex], timeout: 10)
}
for error in [Error.dummy as Swift.Error, Error.cancelled] {
helper(policy: .allErrors, error: error)
}
helper(policy: .allErrorsExceptCancellation, error: Error.dummy)
}
func test__void_specialized_conditional_recover__no_recover() {
func helper(policy: CatchPolicy, error: Error, line: UInt = #line) {
let ex = expectation(description: "")
Promise<Void>(error: error).recover(policy: policy) { err in
throw err
}.catch(policy: .allErrors) {
XCTAssertEqual(error, $0 as? Error)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
for error in [Error.dummy, Error.cancelled] {
helper(policy: .allErrors, error: error)
}
helper(policy: .allErrorsExceptCancellation, error: Error.dummy)
}
func test__void_specialized_conditional_recover__ignores_cancellation_but_fed_cancellation() {
let ex = expectation(description: "")
Promise<Void>(error: Error.cancelled).recover(policy: .allErrorsExceptCancellation) { _ in
XCTFail()
}.catch(policy: .allErrors) {
XCTAssertEqual(Error.cancelled, $0 as? Error)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func test__void_specialized_conditional_recover__fulfilled_path() {
let ex = expectation(description: "")
Promise().recover { _ in
XCTFail()
}.catch { _ in
XCTFail() // this `catch` to ensure we are calling the `recover` variant we think we are
}.finally {
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
}
/// `Promise<T>.recover`
extension CatchableTests {
func test__full_recover() {
func helper(error: Swift.Error) {
let ex = expectation(description: "")
Promise<Int>(error: error).recover { _ in return .value(2) }.done {
XCTAssertEqual($0, 2)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
helper(error: Error.dummy)
helper(error: Error.cancelled)
}
func test__full_recover__fulfilled_path() {
let ex = expectation(description: "")
Promise.value(1).recover { _ in XCTFail(); return .value(2) }.done{
XCTAssertEqual($0, 1)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func test__conditional_recover() {
func helper(policy: CatchPolicy, error: Swift.Error, line: UInt = #line) {
let ex = expectation(description: "")
var x = 0
Promise<Int>(error: error).recover(policy: policy) { err -> Promise<Int> in
guard x < 1 else { throw err }
x += 1
return .value(x)
}.done {
XCTAssertEqual($0, x)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
for error in [Error.dummy as Swift.Error, Error.cancelled] {
helper(policy: .allErrors, error: error)
}
helper(policy: .allErrorsExceptCancellation, error: Error.dummy)
}
func test__conditional_recover__no_recover() {
func helper(policy: CatchPolicy, error: Error, line: UInt = #line) {
let ex = expectation(description: "")
Promise<Int>(error: error).recover(policy: policy) { err -> Promise<Int> in
throw err
}.catch(policy: .allErrors) {
XCTAssertEqual(error, $0 as? Error)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
for error in [Error.dummy, Error.cancelled] {
helper(policy: .allErrors, error: error)
}
helper(policy: .allErrorsExceptCancellation, error: Error.dummy)
}
func test__conditional_recover__ignores_cancellation_but_fed_cancellation() {
let ex = expectation(description: "")
Promise<Int>(error: Error.cancelled).recover(policy: .allErrorsExceptCancellation) { _ -> Promise<Int> in
XCTFail()
return .value(1)
}.catch(policy: .allErrors) {
XCTAssertEqual(Error.cancelled, $0 as? Error)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func test__conditional_recover__fulfilled_path() {
let ex = expectation(description: "")
Promise.value(1).recover { err -> Promise<Int> in
XCTFail()
throw err
}.done {
XCTAssertEqual($0, 1)
ex.fulfill()
}.catch { _ in
XCTFail() // this `catch` to ensure we are calling the `recover` variant we think we are
}
wait(for: [ex], timeout: 10)
}
func testEnsureThen_Error() {
let ex = expectation(description: "")
Promise.value(1).done {
XCTAssertEqual($0, 1)
throw Error.dummy
}.ensureThen {
after(seconds: 0.01)
}.catch {
XCTAssertEqual(Error.dummy, $0 as? Error)
}.finally {
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func testEnsureThen_Value() {
let ex = expectation(description: "")
Promise.value(1).ensureThen {
after(seconds: 0.01)
}.done {
XCTAssertEqual($0, 1)
}.catch { _ in
XCTFail()
}.finally {
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
}
private enum Error: CancellableError {
case dummy
case cancelled
var isCancelled: Bool {
return self == Error.cancelled
}
}

View File

@@ -0,0 +1,70 @@
//
// PMKDefaultDispatchQueue.test.swift
// PromiseKit
//
// Created by David Rodriguez on 4/14/16.
// Copyright © 2016 Max Howell. All rights reserved.
//
import class Foundation.Thread
import PromiseKit
import Dispatch
import XCTest
private enum Error: Swift.Error { case dummy }
class PMKDefaultDispatchQueueTest: XCTestCase {
let myQueue = DispatchQueue(label: "myQueue")
override func setUp() {
// can actually only set the default queue once
// - See: PMKSetDefaultDispatchQueue
conf.Q = (myQueue, myQueue)
}
override func tearDown() {
conf.Q = (.main, .main)
}
func testOverrodeDefaultThenQueue() {
let ex = expectation(description: "resolving")
Promise.value(1).then { _ -> Promise<Void> in
ex.fulfill()
XCTAssertFalse(Thread.isMainThread)
return Promise()
}.silenceWarning()
XCTAssertTrue(Thread.isMainThread)
waitForExpectations(timeout: 1)
}
func testOverrodeDefaultCatchQueue() {
let ex = expectation(description: "resolving")
Promise<Int>(error: Error.dummy).catch { _ in
ex.fulfill()
XCTAssertFalse(Thread.isMainThread)
}
XCTAssertTrue(Thread.isMainThread)
waitForExpectations(timeout: 1)
}
func testOverrodeDefaultAlwaysQueue() {
let ex = expectation(description: "resolving")
Promise.value(1).ensure {
ex.fulfill()
XCTAssertFalse(Thread.isMainThread)
}.silenceWarning()
XCTAssertTrue(Thread.isMainThread)
waitForExpectations(timeout: 1)
}
}

View File

@@ -0,0 +1,22 @@
import PromiseKit
import XCTest
class PMKErrorTests: XCTestCase {
func testCustomStringConvertible() {
XCTAssertNotNil(PMKError.invalidCallingConvention.errorDescription)
XCTAssertNotNil(PMKError.returnedSelf.errorDescription)
XCTAssertNotNil(PMKError.badInput.errorDescription)
XCTAssertNotNil(PMKError.cancelled.errorDescription)
XCTAssertNotNil(PMKError.compactMap(1, Int.self).errorDescription)
XCTAssertNotNil(PMKError.emptySequence.errorDescription)
}
func testCustomDebugStringConvertible() {
XCTAssertFalse(PMKError.invalidCallingConvention.debugDescription.isEmpty)
XCTAssertFalse(PMKError.returnedSelf.debugDescription.isEmpty)
XCTAssertNotNil(PMKError.badInput.debugDescription.isEmpty)
XCTAssertFalse(PMKError.cancelled.debugDescription.isEmpty)
XCTAssertFalse(PMKError.compactMap(1, Int.self).debugDescription.isEmpty)
XCTAssertFalse(PMKError.emptySequence.debugDescription.isEmpty)
}
}

View File

@@ -0,0 +1,33 @@
import PromiseKit
import XCTest
class GuaranteeTests: XCTestCase {
func testInit() {
let ex = expectation(description: "")
Guarantee { seal in
seal(1)
}.done {
XCTAssertEqual(1, $0)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func testWait() {
XCTAssertEqual(after(.milliseconds(100)).map(on: nil){ 1 }.wait(), 1)
}
func testThenMap() {
let ex = expectation(description: "")
Guarantee.value([1, 2, 3])
.thenMap { Guarantee.value($0 * 2) }
.done { values in
XCTAssertEqual([2, 4, 6], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
}

View File

@@ -0,0 +1,38 @@
import PromiseKit
import XCTest
class HangTests: XCTestCase {
func test() {
let ex = expectation(description: "block executed")
do {
let value = try hang(after(seconds: 0.02).then { _ -> Promise<Int> in
ex.fulfill()
return .value(1)
})
XCTAssertEqual(value, 1)
} catch {
XCTFail("Unexpected error")
}
waitForExpectations(timeout: 0)
}
enum Error: Swift.Error {
case test
}
func testError() {
var value = 0
do {
_ = try hang(after(seconds: 0.02).done {
value = 1
throw Error.test
})
XCTAssertEqual(value, 1)
} catch Error.test {
return
} catch {
XCTFail("Unexpected error (expected Error.test)")
}
XCTFail("Expected error but no error was thrown")
}
}

View File

@@ -0,0 +1,139 @@
import PromiseKit
import Dispatch
import XCTest
class PromiseTests: XCTestCase {
func testIsPending() {
XCTAssertTrue(Promise<Void>.pending().promise.isPending)
XCTAssertFalse(Promise().isPending)
XCTAssertFalse(Promise<Void>(error: Error.dummy).isPending)
}
func testIsResolved() {
XCTAssertFalse(Promise<Void>.pending().promise.isResolved)
XCTAssertTrue(Promise().isResolved)
XCTAssertTrue(Promise<Void>(error: Error.dummy).isResolved)
}
func testIsFulfilled() {
XCTAssertFalse(Promise<Void>.pending().promise.isFulfilled)
XCTAssertTrue(Promise().isFulfilled)
XCTAssertFalse(Promise<Void>(error: Error.dummy).isFulfilled)
}
func testIsRejected() {
XCTAssertFalse(Promise<Void>.pending().promise.isRejected)
XCTAssertTrue(Promise<Void>(error: Error.dummy).isRejected)
XCTAssertFalse(Promise().isRejected)
}
@available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *)
func testDispatchQueueAsyncExtensionReturnsPromise() {
let ex = expectation(description: "")
DispatchQueue.global().async(.promise) { () -> Int in
XCTAssertFalse(Thread.isMainThread)
return 1
}.done { one in
XCTAssertEqual(one, 1)
ex.fulfill()
}
waitForExpectations(timeout: 1)
}
@available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *)
func testDispatchQueueAsyncExtensionCanThrowInBody() {
let ex = expectation(description: "")
DispatchQueue.global().async(.promise) { () -> Int in
throw Error.dummy
}.done { _ in
XCTFail()
}.catch { _ in
ex.fulfill()
}
waitForExpectations(timeout: 1)
}
func testCustomStringConvertible() {
XCTAssertEqual(Promise<Int>.pending().promise.debugDescription, "Promise<Int>.pending(handlers: 0)")
XCTAssertEqual(Promise().debugDescription, "Promise<()>.fulfilled(())")
XCTAssertEqual(Promise<String>(error: Error.dummy).debugDescription, "Promise<String>.rejected(Error.dummy)")
XCTAssertEqual("\(Promise<Int>.pending().promise)", "Promise(…Int)")
XCTAssertEqual("\(Promise.value(3))", "Promise(3)")
XCTAssertEqual("\(Promise<Void>(error: Error.dummy))", "Promise(dummy)")
}
func testCannotFulfillWithError() {
// sadly this test proves the opposite :(
// left here so maybe one day we can prevent instantiation of `Promise<Error>`
_ = Promise { seal in
seal.fulfill(Error.dummy)
}
_ = Promise<Error>.pending()
_ = Promise.value(Error.dummy)
_ = Promise().map { Error.dummy }
}
#if swift(>=3.1)
func testCanMakeVoidPromise() {
_ = Promise()
_ = Guarantee()
}
#endif
enum Error: Swift.Error {
case dummy
}
func testThrowInInitializer() {
let p = Promise<Void> { _ in
throw Error.dummy
}
XCTAssertTrue(p.isRejected)
guard let err = p.error, case Error.dummy = err else { return XCTFail() }
}
func testThrowInFirstly() {
let ex = expectation(description: "")
firstly { () -> Promise<Int> in
throw Error.dummy
}.catch {
XCTAssertEqual($0 as? Error, Error.dummy)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func testWait() throws {
let p = after(.milliseconds(100)).then(on: nil){ Promise.value(1) }
XCTAssertEqual(try p.wait(), 1)
do {
let p = after(.milliseconds(100)).map(on: nil){ throw Error.dummy }
try p.wait()
XCTFail()
} catch {
XCTAssertEqual(error as? Error, Error.dummy)
}
}
func testPipeForResolved() {
let ex = expectation(description: "")
Promise.value(1).done {
XCTAssertEqual(1, $0)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
}

View File

@@ -0,0 +1,51 @@
import XCTest
import PromiseKit
class RaceTests: XCTestCase {
func test1() {
let ex = expectation(description: "")
race(after(.milliseconds(10)).then{ Promise.value(1) }, after(seconds: 1).map{ 2 }).done { index in
XCTAssertEqual(index, 1)
ex.fulfill()
}.silenceWarning()
waitForExpectations(timeout: 1, handler: nil)
}
func test2() {
let ex = expectation(description: "")
race(after(seconds: 1).map{ 1 }, after(.milliseconds(10)).map{ 2 }).done { index in
XCTAssertEqual(index, 2)
ex.fulfill()
}
waitForExpectations(timeout: 1, handler: nil)
}
func test1Array() {
let ex = expectation(description: "")
let promises = [after(.milliseconds(10)).map{ 1 }, after(seconds: 1).map{ 2 }]
race(promises).done { index in
XCTAssertEqual(index, 1)
ex.fulfill()
}.silenceWarning()
waitForExpectations(timeout: 1, handler: nil)
}
func test2Array() {
let ex = expectation(description: "")
race(after(seconds: 1).map{ 1 }, after(.milliseconds(10)).map{ 2 }).done { index in
XCTAssertEqual(index, 2)
ex.fulfill()
}
waitForExpectations(timeout: 1, handler: nil)
}
func testEmptyArray() {
let ex = expectation(description: "")
let empty = [Promise<Int>]()
race(empty).catch {
guard case PMKError.badInput = $0 else { return XCTFail() }
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
}

View File

@@ -0,0 +1,27 @@
import PromiseKit
import XCTest
class RegressionTests: XCTestCase {
func testReturningPreviousPromiseWorks() {
// regression test because we were doing this wrong
// in our A+ tests implementation for spec: 2.3.1
do {
let promise1 = Promise()
let promise2 = promise1.then(on: nil) { promise1 }
promise2.catch(on: nil) { _ in XCTFail() }
}
do {
enum Error: Swift.Error { case dummy }
let promise1 = Promise<Void>(error: Error.dummy)
let promise2 = promise1.recover(on: nil) { _ in promise1 }
promise2.catch(on: nil) { err in
if case PMKError.returnedSelf = err {
XCTFail()
}
}
}
}
}

View File

@@ -0,0 +1,166 @@
import PromiseKit
import XCTest
class WrapTests: XCTestCase {
fileprivate class KittenFetcher {
let value: Int?
let error: Error?
init(value: Int?, error: Error?) {
self.value = value
self.error = error
}
func fetchWithCompletionBlock(block: @escaping(Int?, Error?) -> Void) {
after(.milliseconds(20)).done {
block(self.value, self.error)
}
}
func fetchWithCompletionBlock2(block: @escaping(Error?, Int?) -> Void) {
after(.milliseconds(20)).done {
block(self.error, self.value)
}
}
func fetchWithCompletionBlock3(block: @escaping(Int, Error?) -> Void) {
after(.milliseconds(20)).done {
block(self.value ?? -99, self.error)
}
}
func fetchWithCompletionBlock4(block: @escaping(Error?) -> Void) {
after(.milliseconds(20)).done {
block(self.error)
}
}
}
func testSuccess() {
let ex = expectation(description: "")
let kittenFetcher = KittenFetcher(value: 2, error: nil)
Promise { seal in
kittenFetcher.fetchWithCompletionBlock(block: seal.resolve)
}.done {
XCTAssertEqual($0, 2)
ex.fulfill()
}.silenceWarning()
waitForExpectations(timeout: 1)
}
func testError() {
let ex = expectation(description: "")
let kittenFetcher = KittenFetcher(value: nil, error: Error.test)
Promise { seal in
kittenFetcher.fetchWithCompletionBlock(block: seal.resolve)
}.catch { error in
defer { ex.fulfill() }
guard case Error.test = error else {
return XCTFail()
}
}
waitForExpectations(timeout: 1)
}
func testInvalidCallingConvention() {
let ex = expectation(description: "")
let kittenFetcher = KittenFetcher(value: nil, error: nil)
Promise { seal in
kittenFetcher.fetchWithCompletionBlock(block: seal.resolve)
}.catch { error in
defer { ex.fulfill() }
guard case PMKError.invalidCallingConvention = error else {
return XCTFail()
}
}
waitForExpectations(timeout: 1)
}
func testInvertedCallingConvention() {
let ex = expectation(description: "")
let kittenFetcher = KittenFetcher(value: 2, error: nil)
Promise { seal in
kittenFetcher.fetchWithCompletionBlock2(block: seal.resolve)
}.done {
XCTAssertEqual($0, 2)
ex.fulfill()
}.silenceWarning()
waitForExpectations(timeout: 1)
}
func testNonOptionalFirstParameter() {
let ex1 = expectation(description: "")
let kf1 = KittenFetcher(value: 2, error: nil)
Promise { seal in
kf1.fetchWithCompletionBlock3(block: seal.resolve)
}.done {
XCTAssertEqual($0, 2)
ex1.fulfill()
}.silenceWarning()
let ex2 = expectation(description: "")
let kf2 = KittenFetcher(value: -100, error: Error.test)
Promise { seal in
kf2.fetchWithCompletionBlock3(block: seal.resolve)
}.catch { _ in ex2.fulfill() }
wait(for: [ex1, ex2] ,timeout: 1)
}
#if swift(>=3.1)
func testVoidCompletionValue() {
let ex1 = expectation(description: "")
let kf1 = KittenFetcher(value: nil, error: nil)
Promise { seal in
kf1.fetchWithCompletionBlock4(block: seal.resolve)
}.done(ex1.fulfill).silenceWarning()
let ex2 = expectation(description: "")
let kf2 = KittenFetcher(value: nil, error: Error.test)
Promise { seal in
kf2.fetchWithCompletionBlock4(block: seal.resolve)
}.catch { _ in ex2.fulfill() }
wait(for: [ex1, ex2], timeout: 1)
}
#endif
func testIsFulfilled() {
XCTAssertTrue(Promise.value(()).result?.isFulfilled ?? false)
XCTAssertFalse(Promise<Int>(error: Error.test).result?.isFulfilled ?? true)
}
func testPendingPromiseDeallocated() {
// NOTE this doesn't seem to register the `deinit` as covered :(
// BUT putting a breakpoint in the deinit CLEARLY shows it getting covered
class Foo {
let p = Promise<Void>.pending()
var ex: XCTestExpectation!
deinit {
after(.milliseconds(100)).done(ex.fulfill)
}
}
let ex = expectation(description: "")
do {
// for code coverage report for `Resolver.deinit` warning
let foo = Foo()
foo.ex = ex
}
wait(for: [ex], timeout: 10)
}
}
private enum Error: Swift.Error {
case test
}

View File

@@ -0,0 +1,78 @@
import PromiseKit
import Dispatch
import XCTest
class StressTests: XCTestCase {
func testThenDataRace() {
let e1 = expectation(description: "")
//will crash if then doesn't protect handlers
stressDataRace(expectation: e1, stressFunction: { promise in
promise.done { s in
XCTAssertEqual("ok", s)
return
}.silenceWarning()
}, fulfill: { "ok" })
waitForExpectations(timeout: 10, handler: nil)
}
@available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *)
func testThensAreSequentialForLongTime() {
var values = [Int]()
let ex = expectation(description: "")
var promise = DispatchQueue.global().async(.promise){ 0 }
let N = 1000
for x in 1..<N {
promise = promise.then { y -> Guarantee<Int> in
values.append(y)
XCTAssertEqual(x - 1, y)
return DispatchQueue.global().async(.promise) { x }
}
}
promise.done { x in
values.append(x)
XCTAssertEqual(values, (0..<N).map{ $0 })
ex.fulfill()
}
waitForExpectations(timeout: 10, handler: nil)
}
func testZalgoDataRace() {
let e1 = expectation(description: "")
//will crash if zalgo doesn't protect handlers
stressDataRace(expectation: e1, stressFunction: { promise in
promise.done(on: nil) { s in
XCTAssertEqual("ok", s)
}.silenceWarning()
}, fulfill: {
return "ok"
})
waitForExpectations(timeout: 10, handler: nil)
}
}
private enum Error: Swift.Error {
case Dummy
}
private func stressDataRace<T: Equatable>(expectation e1: XCTestExpectation, iterations: Int = 1000, stressFactor: Int = 10, stressFunction: @escaping (Promise<T>) -> Void, fulfill f: @escaping () -> T) {
let group = DispatchGroup()
let queue = DispatchQueue(label: "the.domain.of.Zalgo", attributes: .concurrent)
for _ in 0..<iterations {
let (promise, seal) = Promise<T>.pending()
DispatchQueue.concurrentPerform(iterations: stressFactor) { n in
stressFunction(promise)
}
queue.async(group: group) {
seal.fulfill(f())
}
}
group.notify(queue: queue, execute: e1.fulfill)
}

View File

@@ -0,0 +1,155 @@
import PromiseKit
import Dispatch
import XCTest
class ThenableTests: XCTestCase {
func testGet() {
let ex1 = expectation(description: "")
let ex2 = expectation(description: "")
Promise.value(1).get {
XCTAssertEqual($0, 1)
ex1.fulfill()
}.done {
XCTAssertEqual($0, 1)
ex2.fulfill()
}.silenceWarning()
wait(for: [ex1, ex2], timeout: 10)
}
func testCompactMap() {
let ex = expectation(description: "")
Promise.value(1.0).compactMap {
Int($0)
}.done {
XCTAssertEqual($0, 1)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
func testCompactMapThrows() {
enum E: Error { case dummy }
let ex = expectation(description: "")
Promise.value("a").compactMap { x -> Int in
throw E.dummy
}.catch {
if case E.dummy = $0 {} else {
XCTFail()
}
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func testRejectedPromiseCompactMap() {
enum E: Error { case dummy }
let ex = expectation(description: "")
Promise(error: E.dummy).compactMap {
Int($0)
}.catch {
if case E.dummy = $0 {} else {
XCTFail()
}
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func testPMKErrorCompactMap() {
let ex = expectation(description: "")
Promise.value("a").compactMap {
Int($0)
}.catch {
if case PMKError.compactMap = $0 {} else {
XCTFail()
}
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func testCompactMapValues() {
let ex = expectation(description: "")
Promise.value(["1","2","a","4"]).compactMapValues {
Int($0)
}.done {
XCTAssertEqual([1,2,4], $0)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
func testThenMap() {
let ex = expectation(description: "")
Promise.value([1,2,3,4]).thenMap {
Promise.value($0)
}.done {
XCTAssertEqual([1,2,3,4], $0)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
func testThenFlatMap() {
let ex = expectation(description: "")
Promise.value([1,2,3,4]).thenFlatMap {
Promise.value([$0, $0])
}.done {
XCTAssertEqual([1,1,2,2,3,3,4,4], $0)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
func testLastValueForEmpty() {
XCTAssertTrue(Promise.value([]).lastValue.isRejected)
}
func testFirstValueForEmpty() {
XCTAssertTrue(Promise.value([]).firstValue.isRejected)
}
func testThenOffRejected() {
// surprisingly missing in our CI, mainly due to
// extensive use of `done` in A+ tests since PMK 5
let ex = expectation(description: "")
Promise<Int>(error: PMKError.badInput).then { x -> Promise<Int> in
XCTFail()
return .value(x)
}.catch { _ in
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func testBarrier() {
let ex = expectation(description: "")
let q = DispatchQueue(label: "\(#file):\(#line)", attributes: .concurrent)
Promise.value(1).done(on: q, flags: .barrier) {
XCTAssertEqual($0, 1)
dispatchPrecondition(condition: .onQueueAsBarrier(q))
ex.fulfill()
}.catch { _ in
XCTFail()
}
wait(for: [ex], timeout: 10)
}
func testDispatchFlagsSyntax() {
let ex = expectation(description: "")
let q = DispatchQueue(label: "\(#file):\(#line)", attributes: .concurrent)
Promise.value(1).done(on: q, flags: [.barrier, .inheritQoS]) {
XCTAssertEqual($0, 1)
dispatchPrecondition(condition: .onQueueAsBarrier(q))
ex.fulfill()
}.catch { _ in
XCTFail()
}
wait(for: [ex], timeout: 10)
}
}

View File

@@ -0,0 +1,33 @@
import PromiseKit
extension Promise {
func silenceWarning() {}
}
#if os(Linux)
import func CoreFoundation._CFIsMainThread
extension Thread {
// `isMainThread` is not implemented yet in swift-corelibs-foundation.
static var isMainThread: Bool {
return _CFIsMainThread()
}
}
import XCTest
extension XCTestCase {
func wait(for: [XCTestExpectation], timeout: TimeInterval, file: StaticString = #file, line: UInt = #line) {
#if !(swift(>=4.0) && !swift(>=4.1))
let line = Int(line)
#endif
waitForExpectations(timeout: timeout, file: file, line: line)
}
}
extension XCTestExpectation {
func fulfill() {
fulfill(#file, line: #line)
}
}
#endif

View File

@@ -0,0 +1,159 @@
import XCTest
import PromiseKit
class WhenConcurrentTestCase_Swift: XCTestCase {
func testWhen() {
let e = expectation(description: "")
var numbers = (0..<42).makeIterator()
let squareNumbers = numbers.map { $0 * $0 }
let generator = AnyIterator<Guarantee<Int>> {
guard let number = numbers.next() else {
return nil
}
return after(.milliseconds(10)).map {
return number * number
}
}
when(fulfilled: generator, concurrently: 5).done { numbers in
if numbers == squareNumbers {
e.fulfill()
}
}.silenceWarning()
waitForExpectations(timeout: 3, handler: nil)
}
func testWhenEmptyGenerator() {
let e = expectation(description: "")
let generator = AnyIterator<Promise<Int>> {
return nil
}
when(fulfilled: generator, concurrently: 5).done { numbers in
if numbers.count == 0 {
e.fulfill()
}
}.silenceWarning()
waitForExpectations(timeout: 1, handler: nil)
}
func testWhenGeneratorError() {
enum LocalError: Error {
case Unknown
case DivisionByZero
}
let expectedErrorIndex = 42
let expectedError = LocalError.DivisionByZero
let e = expectation(description: "")
var numbers = (-expectedErrorIndex..<expectedErrorIndex).makeIterator()
let generator = AnyIterator<Promise<Int>> {
guard let number = numbers.next() else {
return nil
}
return after(.milliseconds(10)).then { _ -> Promise<Int> in
if number != 0 {
return Promise(error: expectedError)
} else {
return .value(100500 / number)
}
}
}
when(fulfilled: generator, concurrently: 3)
.catch { error in
guard let error = error as? LocalError else {
return
}
guard case .DivisionByZero = error else {
return
}
e.fulfill()
}
waitForExpectations(timeout: 3, handler: nil)
}
func testWhenConcurrency() {
let expectedConcurrently = 4
var currentConcurrently = 0
var maxConcurrently = 0
let e = expectation(description: "")
var numbers = (0..<42).makeIterator()
let generator = AnyIterator<Promise<Int>> {
currentConcurrently += 1
maxConcurrently = max(maxConcurrently, currentConcurrently)
guard let number = numbers.next() else {
return nil
}
return after(.milliseconds(10)).then(on: .main) { _ -> Promise<Int> in
currentConcurrently -= 1
return .value(number * number)
}
}
when(fulfilled: generator, concurrently: expectedConcurrently).done { _ in
XCTAssertEqual(expectedConcurrently, maxConcurrently)
e.fulfill()
}.silenceWarning()
waitForExpectations(timeout: 3)
}
func testWhenConcurrencyLessThanZero() {
let generator = AnyIterator<Promise<Int>> { XCTFail(); return nil }
let p1 = when(fulfilled: generator, concurrently: 0)
let p2 = when(fulfilled: generator, concurrently: -1)
guard let e1 = p1.error else { return XCTFail() }
guard let e2 = p2.error else { return XCTFail() }
guard case PMKError.badInput = e1 else { return XCTFail() }
guard case PMKError.badInput = e2 else { return XCTFail() }
}
func testStopsDequeueingOnceRejected() {
let ex = expectation(description: "")
enum Error: Swift.Error { case dummy }
var x: UInt = 0
let generator = AnyIterator<Promise<Void>> {
x += 1
switch x {
case 0:
fatalError()
case 1:
return Promise()
case 2:
return Promise(error: Error.dummy)
case _:
XCTFail()
return nil
}
}
when(fulfilled: generator, concurrently: 1).done {
XCTFail("\($0)")
}.catch { error in
ex.fulfill()
}
waitForExpectations(timeout: 3)
}
}

View File

@@ -0,0 +1,41 @@
// Created by Austin Feight on 3/19/16.
// Copyright © 2016 Max Howell. All rights reserved.
import PromiseKit
import XCTest
class JoinTests: XCTestCase {
func testImmediates() {
let successPromise = Promise()
var joinFinished = false
when(resolved: successPromise).done(on: nil) { _ in joinFinished = true }
XCTAssert(joinFinished, "Join immediately finishes on fulfilled promise")
let promise2 = Promise.value(2)
let promise3 = Promise.value(3)
let promise4 = Promise.value(4)
var join2Finished = false
when(resolved: promise2, promise3, promise4).done(on: nil) { _ in join2Finished = true }
XCTAssert(join2Finished, "Join immediately finishes on fulfilled promises")
}
func testFulfilledAfterAllResolve() {
let (promise1, seal1) = Promise<Void>.pending()
let (promise2, seal2) = Promise<Void>.pending()
let (promise3, seal3) = Promise<Void>.pending()
var finished = false
when(resolved: promise1, promise2, promise3).done(on: nil) { _ in finished = true }
XCTAssertFalse(finished, "Not all promises have resolved")
seal1.fulfill(())
XCTAssertFalse(finished, "Not all promises have resolved")
seal2.fulfill(())
XCTAssertFalse(finished, "Not all promises have resolved")
seal3.fulfill(())
XCTAssert(finished, "All promises have resolved")
}
}

View File

@@ -0,0 +1,265 @@
import PromiseKit
import Dispatch
import XCTest
class WhenTests: XCTestCase {
func testEmpty() {
let e1 = expectation(description: "")
let promises: [Promise<Void>] = []
when(fulfilled: promises).done { _ in
e1.fulfill()
}.silenceWarning()
let e2 = expectation(description: "")
when(resolved: promises).done { _ in
e2.fulfill()
}.silenceWarning()
wait(for: [e1, e2], timeout: 1)
}
func testInt() {
let e1 = expectation(description: "")
let p1 = Promise.value(1)
let p2 = Promise.value(2)
let p3 = Promise.value(3)
let p4 = Promise.value(4)
when(fulfilled: [p1, p2, p3, p4]).done { x in
XCTAssertEqual(x[0], 1)
XCTAssertEqual(x[1], 2)
XCTAssertEqual(x[2], 3)
XCTAssertEqual(x[3], 4)
XCTAssertEqual(x.count, 4)
e1.fulfill()
}.silenceWarning()
waitForExpectations(timeout: 1, handler: nil)
}
func testDoubleTuple() {
let e1 = expectation(description: "")
let p1 = Promise.value(1)
let p2 = Promise.value("abc")
when(fulfilled: p1, p2).done{ x, y in
XCTAssertEqual(x, 1)
XCTAssertEqual(y, "abc")
e1.fulfill()
}.silenceWarning()
waitForExpectations(timeout: 1, handler: nil)
}
func testTripleTuple() {
let e1 = expectation(description: "")
let p1 = Promise.value(1)
let p2 = Promise.value("abc")
let p3 = Promise.value( 1.0)
when(fulfilled: p1, p2, p3).done { u, v, w in
XCTAssertEqual(1, u)
XCTAssertEqual("abc", v)
XCTAssertEqual(1.0, w)
e1.fulfill()
}.silenceWarning()
waitForExpectations(timeout: 1, handler: nil)
}
func testQuadrupleTuple() {
let e1 = expectation(description: "")
let p1 = Promise.value(1)
let p2 = Promise.value("abc")
let p3 = Promise.value(1.0)
let p4 = Promise.value(true)
when(fulfilled: p1, p2, p3, p4).done { u, v, w, x in
XCTAssertEqual(1, u)
XCTAssertEqual("abc", v)
XCTAssertEqual(1.0, w)
XCTAssertEqual(true, x)
e1.fulfill()
}.silenceWarning()
waitForExpectations(timeout: 1, handler: nil)
}
func testQuintupleTuple() {
let e1 = expectation(description: "")
let p1 = Promise.value(1)
let p2 = Promise.value("abc")
let p3 = Promise.value(1.0)
let p4 = Promise.value(true)
let p5 = Promise.value("a" as Character)
when(fulfilled: p1, p2, p3, p4, p5).done { u, v, w, x, y in
XCTAssertEqual(1, u)
XCTAssertEqual("abc", v)
XCTAssertEqual(1.0, w)
XCTAssertEqual(true, x)
XCTAssertEqual("a" as Character, y)
e1.fulfill()
}.silenceWarning()
waitForExpectations(timeout: 1, handler: nil)
}
func testVoid() {
let e1 = expectation(description: "")
let p1 = Promise.value(1).done { _ in }
let p2 = Promise.value(2).done { _ in }
let p3 = Promise.value(3).done { _ in }
let p4 = Promise.value(4).done { _ in }
when(fulfilled: p1, p2, p3, p4).done(e1.fulfill).silenceWarning()
waitForExpectations(timeout: 1, handler: nil)
}
func testRejected() {
enum Error: Swift.Error { case dummy }
let e1 = expectation(description: "")
let p1 = after(.milliseconds(100)).map{ true }
let p2: Promise<Bool> = after(.milliseconds(200)).map{ throw Error.dummy }
let p3 = Promise.value(false)
when(fulfilled: p1, p2, p3).catch { _ in
e1.fulfill()
}
waitForExpectations(timeout: 1, handler: nil)
}
func testProgress() {
let ex = expectation(description: "")
XCTAssertNil(Progress.current())
let p1 = after(.milliseconds(10))
let p2 = after(.milliseconds(20))
let p3 = after(.milliseconds(30))
let p4 = after(.milliseconds(40))
let progress = Progress(totalUnitCount: 1)
progress.becomeCurrent(withPendingUnitCount: 1)
when(fulfilled: p1, p2, p3, p4).done { _ in
XCTAssertEqual(progress.completedUnitCount, 1)
ex.fulfill()
}.silenceWarning()
progress.resignCurrent()
waitForExpectations(timeout: 1, handler: nil)
}
func testProgressDoesNotExceed100Percent() {
let ex1 = expectation(description: "")
let ex2 = expectation(description: "")
XCTAssertNil(Progress.current())
let p1 = after(.milliseconds(10))
let p2 = after(.milliseconds(20)).done { throw NSError(domain: "a", code: 1, userInfo: nil) }
let p3 = after(.milliseconds(30))
let p4 = after(.milliseconds(40))
let progress = Progress(totalUnitCount: 1)
progress.becomeCurrent(withPendingUnitCount: 1)
let promise = when(fulfilled: p1, p2, p3, p4)
progress.resignCurrent()
promise.catch { _ in
ex2.fulfill()
}
var x = 0
func finally() {
x += 1
if x == 4 {
XCTAssertLessThanOrEqual(1, progress.fractionCompleted)
XCTAssertEqual(progress.completedUnitCount, 1)
ex1.fulfill()
}
}
let q = DispatchQueue.main
p1.done(on: q, finally)
p2.ensure(on: q, finally).silenceWarning()
p3.done(on: q, finally)
p4.done(on: q, finally)
waitForExpectations(timeout: 1, handler: nil)
}
func testUnhandledErrorHandlerDoesNotFire() {
enum Error: Swift.Error {
case test
}
let ex = expectation(description: "")
let p1 = Promise<Void>(error: Error.test)
let p2 = after(.milliseconds(100))
when(fulfilled: p1, p2).done{ _ in XCTFail() }.catch { error in
XCTAssertTrue(error as? Error == Error.test)
ex.fulfill()
}
waitForExpectations(timeout: 1, handler: nil)
}
func testUnhandledErrorHandlerDoesNotFireForStragglers() {
enum Error: Swift.Error {
case test
case straggler
}
let ex1 = expectation(description: "")
let ex2 = expectation(description: "")
let ex3 = expectation(description: "")
let p1 = Promise<Void>(error: Error.test)
let p2 = after(.milliseconds(100)).done { throw Error.straggler }
let p3 = after(.milliseconds(200)).done { throw Error.straggler }
when(fulfilled: p1, p2, p3).catch { error -> Void in
XCTAssertTrue(Error.test == error as? Error)
ex1.fulfill()
}
p2.ensure { after(.milliseconds(100)).done(ex2.fulfill) }.silenceWarning()
p3.ensure { after(.milliseconds(100)).done(ex3.fulfill) }.silenceWarning()
waitForExpectations(timeout: 1, handler: nil)
}
func testAllSealedRejectedFirstOneRejects() {
enum Error: Swift.Error {
case test1
case test2
case test3
}
let ex = expectation(description: "")
let p1 = Promise<Void>(error: Error.test1)
let p2 = Promise<Void>(error: Error.test2)
let p3 = Promise<Void>(error: Error.test3)
when(fulfilled: p1, p2, p3).catch { error in
XCTAssertTrue(error as? Error == Error.test1)
ex.fulfill()
}
waitForExpectations(timeout: 1)
}
func testGuaranteeWhen() {
let ex1 = expectation(description: "")
when(Guarantee(), Guarantee()).done {
ex1.fulfill()
}
let ex2 = expectation(description: "")
when(guarantees: [Guarantee(), Guarantee()]).done {
ex2.fulfill()
}
wait(for: [ex1, ex2], timeout: 10)
}
}

View File

@@ -0,0 +1,59 @@
import XCTest
import PromiseKit
class ZalgoTests: XCTestCase {
func test1() {
var resolved = false
Promise.value(1).done(on: nil) { _ in
resolved = true
}.silenceWarning()
XCTAssertTrue(resolved)
}
func test2() {
let p1 = Promise.value(1).map(on: nil) { x in
return 2
}
XCTAssertEqual(p1.value!, 2)
var x = 0
let (p2, seal) = Promise<Int>.pending()
p2.done(on: nil) { _ in
x = 1
}.silenceWarning()
XCTAssertEqual(x, 0)
seal.fulfill(1)
XCTAssertEqual(x, 1)
}
// returning a pending promise from its own zalgod then handler doesnt hang
func test3() {
let ex = (expectation(description: ""), expectation(description: ""))
var p1: Promise<Void>!
p1 = after(.milliseconds(100)).then(on: nil) { _ -> Promise<Void> in
ex.0.fulfill()
return p1
}
p1.catch { err in
defer{ ex.1.fulfill() }
guard case PMKError.returnedSelf = err else { return XCTFail() }
}
waitForExpectations(timeout: 1)
}
// return a sealed promise from its own zalgod then handler doesnt hang
func test4() {
let ex = expectation(description: "")
let p1 = Promise.value(1)
p1.then(on: nil) { _ -> Promise<Int> in
ex.fulfill()
return p1
}.silenceWarning()
waitForExpectations(timeout: 1)
}
}