Skip to content

Commit

Permalink
Merge pull request #45 from cx-org/fix#44
Browse files Browse the repository at this point in the history
fix scheduler time overflow
  • Loading branch information
ddddxxx committed Nov 7, 2019
2 parents 969a36a + c8d95ba commit 5c553b9
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 15 deletions.
19 changes: 16 additions & 3 deletions Sources/CXFoundation/DispatchQueue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,21 @@ extension CXWrappers.DispatchQueue: CombineX.Scheduler {
///
/// - Parameter timeInterval: A dispatch time interval.
public init(_ timeInterval: DispatchTimeInterval) {
let now = DispatchTime.now()
self.magnitude = Int((now + timeInterval).uptimeNanoseconds - now.uptimeNanoseconds)
switch timeInterval {
case let .seconds(n):
self.magnitude = n.multipliedClamping(by: Const.nsec_per_sec)
case let .milliseconds(n):
self.magnitude = n.multipliedClamping(by: Const.nsec_per_msec)
case let .microseconds(n):
self.magnitude = n.multipliedClamping(by: Const.nsec_per_usec)
case let .nanoseconds(n):
self.magnitude = n
case .never:
self.magnitude = .max
@unknown default:
let now = DispatchTime.now()
self.magnitude = Int((now + timeInterval).uptimeNanoseconds - now.uptimeNanoseconds)
}
}

/// Creates a dispatch queue time interval from a floating-point seconds value.
Expand All @@ -124,7 +137,7 @@ extension CXWrappers.DispatchQueue: CombineX.Scheduler {
///
/// - Parameter value: The number of seconds, as an `Int`.
public init(integerLiteral value: Int) {
self.magnitude = value * Const.nsec_per_sec
self.init(.seconds(value))
}

/// Creates a dispatch queue time interval from a binary integer type.
Expand Down
26 changes: 26 additions & 0 deletions Sources/CXTestUtility/Predicate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Foundation
import Nimble

public func throwAssertion<T>() -> Predicate<T> {
return Predicate { actualExpression in
return try Nimble.throwAssertion().satisfies(actualExpression.cast { _ in })
}
}

public func beAllEqual<S: Sequence, T: Equatable>() -> Predicate<S>
where S.Iterator.Element == T {
return Predicate.simple("element be all equal") { actualExpression in
guard let actualValue = try actualExpression.evaluate() else {
return .fail
}
var actualGenerator = actualValue.makeIterator()
if let first = actualGenerator.next() {
while let next = actualGenerator.next() {
if next != first {
return .doesNotMatch
}
}
}
return .matches
}
}
8 changes: 0 additions & 8 deletions Sources/CXTestUtility/ThrowAssertionPredicate.swift

This file was deleted.

7 changes: 7 additions & 0 deletions Sources/CXUtility/Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
extension FixedWidthInteger {

public func multipliedClamping(by rhs: Self) -> Self {
let (value, overflow) = multipliedReportingOverflow(by: rhs)
return overflow ? .max : value
}
}
20 changes: 18 additions & 2 deletions Tests/CXFoundationTests/SchedulerSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,30 @@ class SchedulerSpec: QuickSpec {

// MARK: 1.1 should schedule, (we just need it to compile now, and yes! we did it! 🤣)
it("should schedule") {
let dt: CXWrappers.DispatchQueue.SchedulerTimeType.Stride = 0.1
_ = Just(1)
.receive(on: RunLoop.main.cx)
.receive(on: DispatchQueue.main.cx)
.receive(on: OperationQueue.main.cx)
.delay(for: dt, scheduler: DispatchQueue.main.cx)
.sink { _ in
}
}

// MARK: 2.1 should clamp overflowing schedule time, instead of crash.
it("should clamp overflowing schedule time") {
let dts: [CXWrappers.DispatchQueue.SchedulerTimeType.Stride] = [
.seconds(.max),
.milliseconds(.max),
.microseconds(.max),
.nanoseconds(.max),
]

expect(dts).to(beAllEqual())

#if !SWIFT_PACKAGE
expect {
CXWrappers.DispatchQueue.SchedulerTimeType.Stride.seconds(.infinity)
}.to(throwAssertion())
#endif
}
}
}
28 changes: 28 additions & 0 deletions Tests/CXInconsistentTests/Fixed/FixedSpec.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import Foundation
import CXShim
import CXTestUtility
import Quick
import Nimble

class FixedSpec: QuickSpec {

override func spec() {

afterEach {
TestResources.release()
}

// MARK: should fix https://github.com/cx-org/CombineX/issues/44
it("should fix #44") {
let pub = PassthroughSubject<Int, Never>()
let sub = makeTestSubscriber(Int.self, Never.self, .unlimited)
pub.debounce(for: 0.5, scheduler: DispatchQueue.global().cx).receive(subscriber: sub)

(0...10).forEach(pub.send)

expect(sub.events).to(beEmpty())
expect(sub.events).toEventually(equal([.value(10)]))
}
}
}

8 changes: 6 additions & 2 deletions Tests/CXInconsistentTests/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
This target contains inconsistent behaviour between CombineX and Apple's Combine, including:

### [FailingTests](FailingTests)
### FailingTests

The behaviour of CombineX is wrong and needs to be fixed.

### [SuspiciousBehaviour](SuspiciousBehaviour)
### SuspiciousBehaviour

The behaviour of Apple's Combine is suspicious or inconsistent with documentation. CombineX will not fix them.

### Fixed

Was failing or suspicious, now fixed and consistent with Apple's Combine.
2 changes: 2 additions & 0 deletions Tests/LinuxMain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,6 @@ QCKMain([
FailingSubjectSpec.self,

SuspiciousDemandSpec.self,

FixedSpec.self,
])

0 comments on commit 5c553b9

Please sign in to comment.