Last active
October 15, 2019 20:41
-
-
Save devxoul/7ac122594c18ea5bc4edbab9899c824a to your computer and use it in GitHub Desktop.
Verify RxSwift resources and scheduler actions after finishing the tests.
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
import Quick | |
import RxSwift | |
final class MySpec: QuickSpec { | |
override func spec() { | |
verifyRxResourcesAreReleased() | |
it("fails") { | |
_ = Observable<Int>.interval(.seconds(1), scheduler: MainScheduler.instance).subscribe() | |
} | |
it("passes") { | |
_ = Observable<Int>.just(10) | |
} | |
} | |
} |
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
import Nimble | |
@testable import Quick | |
@testable import RxSwift | |
// MARK: Verifyinig Number of RxSwift Resources | |
func verifyRxResourcesAreReleased( | |
beforeEach: @escaping (@escaping BeforeExampleClosure) -> Void = Quick.beforeEach, | |
afterEach: @escaping (@escaping AfterExampleClosure) -> Void = Quick.afterEach | |
) { | |
var numberOfResourcesByExampleIndex: [Example.Index: Int32] = [:] | |
beforeEach { | |
let example = currentExample() | |
let numberOfResources = RxSwift.Resources.total | |
numberOfResourcesByExampleIndex[example.index] = numberOfResources | |
} | |
afterEach { | |
let example = currentExample() | |
let expectedNumberOfResources = numberOfResourcesByExampleIndex.removeValue(forKey: example.index)! | |
let currentNumberOfResources = RxSwift.Resources.total | |
let expectation = expect(currentNumberOfResources, file: example.file, line: example.line) | |
expectation.to(haveLessThanOrEqualTo(expectedNumberOfResources, resource: "Rx resources")) | |
} | |
} | |
func verifyRxResourcesAreReleased(configuration: Configuration) { | |
verifyRxResourcesAreReleased(beforeEach: configuration.beforeEach, afterEach: configuration.afterEach) | |
} | |
// MARK: Verifyinig Number of Actions in Main Scheduler | |
func verifyMainSchedulerIsEmpty( | |
beforeEach: @escaping (@escaping BeforeExampleClosure) -> Void = Quick.beforeEach, | |
afterEach: @escaping (@escaping AfterExampleClosure) -> Void = Quick.afterEach | |
) { | |
var numberOfActionsByExampleIndex: [Example.Index: Int32] = [:] | |
beforeEach { | |
let example = currentExample() | |
let numberOfActions = MainScheduler.instance.numberEnqueued.value | |
numberOfActionsByExampleIndex[example.index] = numberOfActions | |
} | |
afterEach { | |
let example = currentExample() | |
let expectedNumberOfActions = numberOfActionsByExampleIndex.removeValue(forKey: example.index)! | |
let currentNumberOfActions = MainScheduler.instance.numberEnqueued.value | |
let expectation = expect(currentNumberOfActions, file: example.file, line: example.line) | |
expectation.to(haveLessThanOrEqualTo(expectedNumberOfActions, resource: "scheduled actions in MainScheduler")) | |
} | |
} | |
func verifyMainSchedulerIsEmpty(configuration: Configuration) { | |
verifyMainSchedulerIsEmpty(beforeEach: configuration.beforeEach, afterEach: configuration.afterEach) | |
} | |
private extension AtomicInt { | |
var value: Int32 { | |
let mirror = Mirror(reflecting: self) | |
return mirror.children.first { name, _ in name == "value" }!.value as! Int32 | |
} | |
} | |
// MARK: Example | |
private struct Example { | |
typealias Index = Int | |
let index: Index | |
let file: FileString | |
let line: UInt | |
} | |
private func currentExample() -> Example { | |
let metadata = World.sharedWorld.currentExampleMetadata! | |
let callsite = metadata.example.callsite | |
return Example(index: metadata.exampleIndex, file: callsite.file, line: callsite.line) | |
} | |
// MARK: Matchers | |
private func haveLessThanOrEqualTo<T>(_ expectedCount: T, resource: String) -> Predicate<T> where T: Comparable { | |
let failureMessage = "have less than or equal to <\(stringify(expectedCount))> \(resource) after finishing the test" | |
return Predicate.define(failureMessage) { actualExpression, msg in | |
let actualValue = try actualExpression.evaluate() | |
switch (expectedCount, actualValue) { | |
case (nil, _?): | |
return PredicateResult(status: .fail, message: msg.appendedBeNilHint()) | |
case (nil, nil), (_, nil): | |
return PredicateResult(status: .fail, message: msg) | |
case (let expected, let actual?): | |
let matches = actual <= expected | |
return PredicateResult(bool: matches, message: msg) | |
} | |
} | |
} |
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
import Quick | |
class TestConfiguration: QuickConfiguration { | |
override class func configure(_ configuration: Configuration) { | |
// This will break the tests because: | |
// * `RxSwift.Resourecs.total` doesn't decrease in a specific case: https://github.com/ReactiveX/RxSwift/pull/2091 | |
// * `UIViewController` is not deallocated synchronously: https://gist.github.com/devxoul/a376db02653ccff732d365636d486f03 | |
verifyRxResourcesAreReleased(configuration: configuration) | |
// ...so, use this first | |
verifyMainSchedulerIsEmpty(configuration: configuration) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment