Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix debounce when started twice #772

Merged
merged 4 commits into from
Feb 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# master
*Please add new entries at the top.*

1. Fixed `SignalProducer.debounce` operator that, when started more than once, would not deliver values on producers started after the first time. (#772, kudos to @gpambrozio)
1. `FlattenStrategy.throttle` is introduced. (#713, kudos to @inamiy)
1. Updated `README.md` to reflect Swift 5.1 compatibility and point snippets to 6.1.0 (#763, kudos to @Marcocanc)
1. Update travis to Xcode 11.1 and Swift 5.1 (#764, kudos @petrpavlik)
Expand Down
3 changes: 1 addition & 2 deletions Sources/Event.swift
Original file line number Diff line number Diff line change
Expand Up @@ -870,9 +870,8 @@ extension Signal.Event {
internal static func debounce(_ interval: TimeInterval, on scheduler: DateScheduler, discardWhenCompleted: Bool) -> Transformation<Value, Error> {
precondition(interval >= 0)

let state: Atomic<ThrottleState<Value>> = Atomic(ThrottleState(previousDate: scheduler.currentDate, pendingValue: nil))

return { action, lifetime in
let state: Atomic<ThrottleState<Value>> = Atomic(ThrottleState(previousDate: scheduler.currentDate, pendingValue: nil))
let d = SerialDisposable()

lifetime.observeEnded {
Expand Down
269 changes: 269 additions & 0 deletions Tests/ReactiveSwiftTests/SignalProducerSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1167,6 +1167,275 @@ class SignalProducerSpec: QuickSpec {
}
}

describe("debounce discarding the latest value when terminated") {
var scheduler: TestScheduler!
var observer: Signal<Int, Never>.Observer!
var producer: SignalProducer<Int, Never>!

beforeEach {
scheduler = TestScheduler()

let (baseSignal, baseObserver) = Signal<Int, Never>.pipe()
observer = baseObserver

producer = SignalProducer(baseSignal)
.debounce(0.1, on: scheduler)

expect(producer).notTo(beNil())
}

it("should send values on the given scheduler once the interval has passed since the last value was sent") {
var values: [Int] = []
producer.startWithValues { value in
values.append(value)
}

expect(values) == []

observer.send(value: 0)
expect(values) == []

scheduler.advance()
expect(values) == []

observer.send(value: 1)
observer.send(value: 2)
expect(values) == []

scheduler.advance(by: .milliseconds(1500))
expect(values) == [ 2 ]

scheduler.advance(by: .seconds(3))
expect(values) == [ 2 ]

observer.send(value: 3)
expect(values) == [ 2 ]

scheduler.advance()
expect(values) == [ 2 ]

observer.send(value: 4)
observer.send(value: 5)
scheduler.advance()
expect(values) == [ 2 ]

scheduler.run()
expect(values) == [ 2, 5 ]
}

it("should schedule completion immediately") {
var values: [Int] = []
var completed = false

producer.on(event: { event in
switch event {
case let .value(value):
values.append(value)
case .completed:
completed = true
default:
break
}
}).start()

observer.send(value: 0)
scheduler.advance()
expect(values) == []

observer.send(value: 1)
observer.sendCompleted()
expect(completed) == false

scheduler.advance()
expect(values) == []
expect(completed) == true

scheduler.run()
expect(values) == []
expect(completed) == true
}

context("starting the producer twice") {
it("should deviver the same values") {
var values1: [Int] = []
var values2: [Int] = []
producer.startWithValues { value in
values1.append(value)
}
producer.startWithValues { value in
values2.append(value)
}

expect(values1) == []
expect(values2) == []

observer.send(value: 1)
observer.send(value: 2)

scheduler.advance(by: .milliseconds(1500))
expect(values1) == [ 2 ]
expect(values2) == [ 2 ]

observer.send(value: 3)
scheduler.advance(by: .milliseconds(1500))
expect(values1) == [ 2, 3 ]
expect(values2) == [ 2, 3 ]

observer.send(value: 4)
observer.sendCompleted()
scheduler.run()

expect(values1) == [ 2, 3 ]
expect(values2) == [ 2, 3 ]
}
}
}

describe("debounce without discarding the latest value when terminated") {
var scheduler: TestScheduler!
var observer: Signal<Int, Never>.Observer!
var producer: SignalProducer<Int, Never>!

beforeEach {
scheduler = TestScheduler()

let (baseSignal, baseObserver) = Signal<Int, Never>.pipe()
observer = baseObserver

producer = SignalProducer(baseSignal)
.debounce(0.1, on: scheduler, discardWhenCompleted: false)

expect(producer).notTo(beNil())
}

it("should send values on the given scheduler once the interval has passed since the last value was sent") {
var values: [Int] = []
producer.startWithValues { value in
values.append(value)
}

expect(values) == []

observer.send(value: 0)
expect(values) == []

scheduler.advance()
expect(values) == []

observer.send(value: 1)
observer.send(value: 2)
expect(values) == []

scheduler.advance(by: .milliseconds(1500))
expect(values) == [ 2 ]

scheduler.advance(by: .seconds(3))
expect(values) == [ 2 ]

observer.send(value: 3)
expect(values) == [ 2 ]

scheduler.advance()
expect(values) == [ 2 ]

observer.send(value: 4)
observer.send(value: 5)
scheduler.advance()
expect(values) == [ 2 ]
observer.sendCompleted()

scheduler.run()
expect(values) == [ 2, 5 ]

}

it("should schedule completion after sending the last value") {
var values: [Int] = []
var completed = false

producer.on(event: { event in
switch event {
case let .value(value):
values.append(value)
case .completed:
completed = true
default:
break
}
}).start()

observer.send(value: 0)
scheduler.advance()
expect(values) == []

observer.send(value: 1)
scheduler.advance()
observer.sendCompleted()
expect(completed) == false

scheduler.advance()
expect(values) == []
expect(completed) == false

scheduler.run()
expect(values) == [1]
expect(completed) == true
}

it("should schedule completion immediately if there is no pending value") {
var completed = false

producer.on(event: { event in
switch event {
case .completed:
completed = true
default:
break
}
}).start()

observer.sendCompleted()
expect(completed) == false
scheduler.advance()
expect(completed) == true
}

context("starting the producer twice") {
it("should deviver the same values") {
var values1: [Int] = []
var values2: [Int] = []
producer.startWithValues { value in
values1.append(value)
}
producer.startWithValues { value in
values2.append(value)
}

expect(values1) == []
expect(values2) == []

observer.send(value: 1)
observer.send(value: 2)

scheduler.advance(by: .milliseconds(1500))
expect(values1) == [ 2 ]
expect(values2) == [ 2 ]

observer.send(value: 3)
scheduler.advance(by: .milliseconds(1500))
expect(values1) == [ 2, 3 ]
expect(values2) == [ 2, 3 ]

observer.send(value: 4)
observer.sendCompleted()
scheduler.run()

expect(values1) == [ 2, 3, 4 ]
expect(values2) == [ 2, 3, 4 ]
}
}
}

describe("on") {
it("should attach event handlers to each started signal") {
let (baseProducer, observer) = SignalProducer<Int, TestError>.pipe()
Expand Down