Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/swift/3.0' into swift/4.0
Browse files Browse the repository at this point in the history
  • Loading branch information
inamiy committed Oct 4, 2017
2 parents 5c335e1 + e538f8e commit c66f0f7
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 14 deletions.
42 changes: 28 additions & 14 deletions SwiftTask/SwiftTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@ open class Task<Progress, Value, Error>: Cancellable, CustomStringConvertible
self.name = "RejectedTask"
}

private convenience init(_errorInfo: ErrorInfo)
{
self.init(_initClosure: { _, progress, fulfill, _reject, configure in
_reject(_errorInfo)
})
self.name = "RejectedTask"
}

///
/// Create promise-like task which only allows fulfill & reject (no progress & configure)
///
Expand Down Expand Up @@ -312,16 +320,22 @@ open class Task<Progress, Value, Error>: Cancellable, CustomStringConvertible
}

/// Returns new task that is retryable for `maxRetryCount (= maxTryCount-1)` times.
public func retry(_ maxRetryCount: Int) -> Task
/// - Parameter condition: Predicate that will be evaluated on each retry timing.
public func retry(_ maxRetryCount: Int, condition: @escaping ((ErrorInfo) -> Bool) = { _ in true }) -> Task
{
if maxRetryCount < 1 { return self }
guard maxRetryCount > 0 else { return self }

return Task { machine, progress, fulfill, _reject, configure in

let task = self.progress { _, progressValue in
progress(progressValue)
}.failure { [unowned self] _ -> Task in
return self.clone().retry(maxRetryCount-1) // clone & try recursively
}.failure { [unowned self] errorInfo -> Task in
if condition(errorInfo) {
return self.clone().retry(maxRetryCount-1, condition: condition) // clone & try recursively
}
else {
return Task(_errorInfo: errorInfo)
}
}

task.progress { _, progressValue in
Expand Down Expand Up @@ -362,7 +376,7 @@ open class Task<Progress, Value, Error>: Cancellable, CustomStringConvertible
return self.progress(&dummyCanceller, progressClosure)
}

public func progress<C: Canceller>(_ canceller: inout C?, _ progressClosure: @escaping (ProgressTuple) -> Void) -> Self
@discardableResult public func progress<C: Canceller>(_ canceller: inout C?, _ progressClosure: @escaping (ProgressTuple) -> Void) -> Self
{
var token: _HandlerToken? = nil
self._machine.addProgressTupleHandler(&token, progressClosure)
Expand All @@ -389,7 +403,7 @@ open class Task<Progress, Value, Error>: Cancellable, CustomStringConvertible
return self.then(&dummyCanceller, thenClosure)
}

public func then<Value2, C: Canceller>(_ canceller: inout C?, _ thenClosure: @escaping (Value?, ErrorInfo?) -> Value2) -> Task<Progress, Value2, Error>
@discardableResult public func then<Value2, C: Canceller>(_ canceller: inout C?, _ thenClosure: @escaping (Value?, ErrorInfo?) -> Value2) -> Task<Progress, Value2, Error>
{
return self.then(&canceller) { (value, errorInfo) -> Task<Progress, Value2, Error> in
return Task<Progress, Value2, Error>(value: thenClosure(value, errorInfo))
Expand All @@ -404,7 +418,7 @@ open class Task<Progress, Value, Error>: Cancellable, CustomStringConvertible
///
/// - Returns: New `Task`
///
public func then<Progress2, Value2, Error2>(_ thenClosure: @escaping (Value?, ErrorInfo?) -> Task<Progress2, Value2, Error2>) -> Task<Progress2, Value2, Error2>
@discardableResult public func then<Progress2, Value2, Error2>(_ thenClosure: @escaping (Value?, ErrorInfo?) -> Task<Progress2, Value2, Error2>) -> Task<Progress2, Value2, Error2>
{
var dummyCanceller: Canceller? = nil
return self.then(&dummyCanceller, thenClosure)
Expand All @@ -418,7 +432,7 @@ open class Task<Progress, Value, Error>: Cancellable, CustomStringConvertible
//
/// - Returns: New `Task`
///
public func then<Progress2, Value2, Error2, C: Canceller>(_ canceller: inout C?, _ thenClosure: @escaping (Value?, ErrorInfo?) -> Task<Progress2, Value2, Error2>) -> Task<Progress2, Value2, Error2>
@discardableResult public func then<Progress2, Value2, Error2, C: Canceller>(_ canceller: inout C?, _ thenClosure: @escaping (Value?, ErrorInfo?) -> Task<Progress2, Value2, Error2>) -> Task<Progress2, Value2, Error2>
{
return Task<Progress2, Value2, Error2> { [unowned self, weak canceller] newMachine, progress, fulfill, _reject, configure in

Expand Down Expand Up @@ -469,7 +483,7 @@ open class Task<Progress, Value, Error>: Cancellable, CustomStringConvertible
return self.success(&dummyCanceller, successClosure)
}

public func success<Value2, C: Canceller>(_ canceller: inout C?, _ successClosure: @escaping (Value) -> Value2) -> Task<Progress, Value2, Error>
@discardableResult public func success<Value2, C: Canceller>(_ canceller: inout C?, _ successClosure: @escaping (Value) -> Value2) -> Task<Progress, Value2, Error>
{
return self.success(&canceller) { (value: Value) -> Task<Progress, Value2, Error> in
return Task<Progress, Value2, Error>(value: successClosure(value))
Expand All @@ -484,13 +498,13 @@ open class Task<Progress, Value, Error>: Cancellable, CustomStringConvertible
///
/// - Returns: New `Task`
///
public func success<Progress2, Value2, Error2>(_ successClosure: @escaping (Value) -> Task<Progress2, Value2, Error2>) -> Task<Progress2, Value2, Error>
@discardableResult public func success<Progress2, Value2, Error2>(_ successClosure: @escaping (Value) -> Task<Progress2, Value2, Error2>) -> Task<Progress2, Value2, Error>
{
var dummyCanceller: Canceller? = nil
return self.success(&dummyCanceller, successClosure)
}

public func success<Progress2, Value2, Error2, C: Canceller>(_ canceller: inout C?, _ successClosure: @escaping (Value) -> Task<Progress2, Value2, Error2>) -> Task<Progress2, Value2, Error>
@discardableResult public func success<Progress2, Value2, Error2, C: Canceller>(_ canceller: inout C?, _ successClosure: @escaping (Value) -> Task<Progress2, Value2, Error2>) -> Task<Progress2, Value2, Error>
{
var localCanceller = canceller; defer { canceller = localCanceller }
return Task<Progress2, Value2, Error> { [unowned self] newMachine, progress, fulfill, _reject, configure in
Expand Down Expand Up @@ -526,7 +540,7 @@ open class Task<Progress, Value, Error>: Cancellable, CustomStringConvertible
return self.failure(&dummyCanceller, failureClosure)
}

public func failure<C: Canceller>(_ canceller: inout C?, _ failureClosure: @escaping (ErrorInfo) -> Value) -> Task
@discardableResult public func failure<C: Canceller>(_ canceller: inout C?, _ failureClosure: @escaping (ErrorInfo) -> Value) -> Task
{
return self.failure(&canceller) { (errorInfo: ErrorInfo) -> Task in
return Task(value: failureClosure(errorInfo))
Expand All @@ -542,13 +556,13 @@ open class Task<Progress, Value, Error>: Cancellable, CustomStringConvertible
///
/// - Returns: New `Task`
///
public func failure<Progress2, Error2>(_ failureClosure: @escaping (ErrorInfo) -> Task<Progress2, Value, Error2>) -> Task<Progress2, Value, Error2>
@discardableResult public func failure<Progress2, Error2>(_ failureClosure: @escaping (ErrorInfo) -> Task<Progress2, Value, Error2>) -> Task<Progress2, Value, Error2>
{
var dummyCanceller: Canceller? = nil
return self.failure(&dummyCanceller, failureClosure)
}

public func failure<Progress2, Error2, C: Canceller>(_ canceller: inout C?, _ failureClosure: @escaping (ErrorInfo) -> Task<Progress2, Value, Error2>) -> Task<Progress2, Value, Error2>
@discardableResult public func failure<Progress2, Error2, C: Canceller>(_ canceller: inout C?, _ failureClosure: @escaping (ErrorInfo) -> Task<Progress2, Value, Error2>) -> Task<Progress2, Value, Error2>
{
var localCanceller = canceller; defer { canceller = localCanceller }
return Task<Progress2, Value, Error2> { [unowned self] newMachine, progress, fulfill, _reject, configure in
Expand Down
35 changes: 35 additions & 0 deletions SwiftTaskTests/SwiftTaskTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,41 @@ class SwiftTaskTests: _TestCase
XCTAssertEqual(actualTryCount, maxTryCount, "`actualTryCount` should reach `maxTryCount` because task keeps rejected and never fulfilled.")
}

func testRetry_condition()
{
// NOTE: this is async test
if !self.isAsync { return }

let expect = self.expectation(description: #function)
let maxTryCount = 4
var actualTryCount = 0

Task<Float, String, ErrorString> { progress, fulfill, reject, configure in

self.perform {
actualTryCount += 1
reject("ERROR \(actualTryCount)")
}

}.retry(maxTryCount-1) { error, isCancelled -> Bool in
XCTAssertNotEqual(error!, "ERROR 0", "Should not evaluate retry condition on first try. It is not retry.")
return actualTryCount < 3
}.failure { error, isCancelled -> String in

XCTAssertEqual(error!, "ERROR 3")
XCTAssertFalse(isCancelled)

expect.fulfill()

return "DUMMY"

}

self.wait()

XCTAssertEqual(actualTryCount, 3)
}

func testRetry_progress()
{
// NOTE: this is async test
Expand Down

0 comments on commit c66f0f7

Please sign in to comment.