This repository has been archived by the owner on Aug 2, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
308 additions
and
2 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
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,33 @@ | ||
import Foundation | ||
|
||
/** | ||
Retries a given Future for a given number of times | ||
- parameter count: How many times you want the future to be retried if failed (No retries: 0) | ||
- parameter every: How much you want to wait before retrying | ||
- parameter futureClosure: The closure generating a new instance of the future to retry | ||
- returns: A future that fails if all the generated futures have failed, or succeeds if one of the generated futures succeeds | ||
*/ | ||
public func retry<T>(count: Int, every delay: NSTimeInterval, futureClosure: Void -> Future<T>) -> Future<T> { | ||
if count <= 0 { | ||
return futureClosure() | ||
} | ||
|
||
let result = Promise<T>() | ||
|
||
result.mimic( | ||
futureClosure() | ||
.recover { Void -> Future<T> in | ||
let delayed = Promise<T>() | ||
|
||
GCD.delay(delay, closure: {}).onSuccess { | ||
delayed.mimic(retry(count - 1, every: delay, futureClosure: futureClosure)) | ||
} | ||
|
||
return delayed.future | ||
} | ||
) | ||
|
||
return result.future | ||
} |
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
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
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,247 @@ | ||
import Foundation | ||
import Quick | ||
import Nimble | ||
import PiedPiper | ||
|
||
class FutureRetryTests: QuickSpec { | ||
override func spec() { | ||
describe("Retrying a given future") { | ||
var lastPromise: Promise<Int>? | ||
var retryCount: Int! | ||
let futureClosure: Void -> Future<Int> = { | ||
lastPromise = Promise() | ||
retryCount = retryCount + 1 | ||
return lastPromise!.future | ||
} | ||
var result: Future<Int>! | ||
var successSentinel: Int? | ||
var failureSentinel: ErrorType? | ||
var cancelSentinel: Bool! | ||
|
||
beforeEach { | ||
lastPromise = nil | ||
retryCount = 0 | ||
|
||
successSentinel = nil | ||
failureSentinel = nil | ||
cancelSentinel = false | ||
} | ||
|
||
context("when retrying less than 0 times") { | ||
beforeEach { | ||
result = retry(-1, every: 0, futureClosure: futureClosure) | ||
.onSuccess { | ||
successSentinel = $0 | ||
} | ||
.onFailure { | ||
failureSentinel = $0 | ||
} | ||
.onCancel { | ||
cancelSentinel = true | ||
} | ||
} | ||
|
||
it("should call the closure once") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
context("when the future succeeds") { | ||
let value = 2 | ||
|
||
beforeEach { | ||
lastPromise?.succeed(value) | ||
} | ||
|
||
it("should not retry") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
it("should succeed the result") { | ||
expect(successSentinel).notTo(beNil()) | ||
} | ||
|
||
it("should succeed with the right value") { | ||
expect(successSentinel).to(equal(value)) | ||
} | ||
} | ||
|
||
context("when the future fails") { | ||
let error = TestError.AnotherError | ||
|
||
beforeEach { | ||
lastPromise?.fail(error) | ||
} | ||
|
||
it("should not retry") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
it("should fail the result") { | ||
expect(failureSentinel).notTo(beNil()) | ||
} | ||
|
||
it("should fail with the right error") { | ||
expect(failureSentinel as? TestError).to(equal(error)) | ||
} | ||
} | ||
|
||
context("when the future is canceled") { | ||
beforeEach { | ||
lastPromise?.cancel() | ||
} | ||
|
||
it("should not retry") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
it("should cancel the result") { | ||
expect(cancelSentinel).to(beTrue()) | ||
} | ||
} | ||
} | ||
|
||
context("when retrying 0 times") { | ||
beforeEach { | ||
result = retry(0, every: 0, futureClosure: futureClosure) | ||
.onSuccess { | ||
successSentinel = $0 | ||
} | ||
.onFailure { | ||
failureSentinel = $0 | ||
} | ||
.onCancel { | ||
cancelSentinel = true | ||
} | ||
} | ||
|
||
it("should call the closure once") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
context("when the future succeeds") { | ||
let value = 2 | ||
|
||
beforeEach { | ||
lastPromise?.succeed(value) | ||
} | ||
|
||
it("should not retry") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
it("should succeed the result") { | ||
expect(successSentinel).notTo(beNil()) | ||
} | ||
|
||
it("should succeed with the right value") { | ||
expect(successSentinel).to(equal(value)) | ||
} | ||
} | ||
|
||
context("when the future fails") { | ||
let error = TestError.AnotherError | ||
|
||
beforeEach { | ||
lastPromise?.fail(error) | ||
} | ||
|
||
it("should not retry") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
it("should fail the result") { | ||
expect(failureSentinel).notTo(beNil()) | ||
} | ||
|
||
it("should fail with the right error") { | ||
expect(failureSentinel as? TestError).to(equal(error)) | ||
} | ||
} | ||
|
||
context("when the future is canceled") { | ||
beforeEach { | ||
lastPromise?.cancel() | ||
} | ||
|
||
it("should not retry") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
it("should cancel the result") { | ||
expect(cancelSentinel).to(beTrue()) | ||
} | ||
} | ||
} | ||
|
||
context("when retrying 1 time") { | ||
beforeEach { | ||
result = retry(1, every: 0.2, futureClosure: futureClosure) | ||
.onSuccess { | ||
successSentinel = $0 | ||
} | ||
.onFailure { | ||
failureSentinel = $0 | ||
} | ||
.onCancel { | ||
cancelSentinel = true | ||
} | ||
} | ||
|
||
it("should call the closure once") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
context("when the future succeeds") { | ||
let value = 2 | ||
|
||
beforeEach { | ||
lastPromise?.succeed(value) | ||
} | ||
|
||
it("should not retry") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
it("should succeed the result") { | ||
expect(successSentinel).notTo(beNil()) | ||
} | ||
|
||
it("should succeed with the right value") { | ||
expect(successSentinel).to(equal(value)) | ||
} | ||
} | ||
|
||
context("when the future fails") { | ||
let error = TestError.AnotherError | ||
|
||
beforeEach { | ||
lastPromise?.fail(error) | ||
} | ||
|
||
it("should not fail the result") { | ||
expect(failureSentinel).to(beNil()) | ||
} | ||
|
||
// FIXME: Failing for some reason :( | ||
xit("should retry") { | ||
expect(retryCount).toEventually(equal(2), timeout: 0.25) | ||
} | ||
} | ||
|
||
context("when the future is canceled") { | ||
beforeEach { | ||
lastPromise?.cancel() | ||
} | ||
|
||
it("should not retry") { | ||
expect(retryCount).to(equal(1)) | ||
} | ||
|
||
it("should cancel the result") { | ||
expect(cancelSentinel).to(beTrue()) | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
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