-
Notifications
You must be signed in to change notification settings - Fork 90
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
1 parent
e73ab05
commit 2e17260
Showing
8 changed files
with
313 additions
and
57 deletions.
There are no files selected for viewing
52 changes: 52 additions & 0 deletions
52
binder/src/main/java/com/badoo/binder/AccumulatorSubject.kt
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,52 @@ | ||
package com.badoo.binder | ||
|
||
import io.reactivex.ObservableSource | ||
import io.reactivex.Observer | ||
import io.reactivex.functions.Consumer | ||
import io.reactivex.subjects.PublishSubject | ||
import java.util.concurrent.atomic.AtomicBoolean | ||
|
||
interface Drainable { | ||
fun drain() | ||
} | ||
|
||
class AccumulatorSubject<T : Any>( | ||
private val initialState: T? = null | ||
) : ObservableSource<T>, Consumer<T>, Drainable { | ||
|
||
private val items: MutableList<T> = mutableListOf() | ||
private val events: PublishSubject<T> = PublishSubject.create() | ||
|
||
private var drained = AtomicBoolean(false) | ||
|
||
init { | ||
initialState?.also { items.add(it) } | ||
} | ||
|
||
override fun subscribe(observer: Observer<in T>) { | ||
events.subscribe(observer) | ||
if (!drained.get()) { | ||
items.forEach { observer.onNext(it) } | ||
} else { | ||
initialState?.also { observer.onNext(it) } | ||
} | ||
} | ||
|
||
override fun accept(value: T) { | ||
if (!drained.get()) { | ||
items.add(value) | ||
} | ||
events.onNext(value) | ||
} | ||
|
||
override fun drain() { | ||
if (!drained.get()) { | ||
drained.set(true) | ||
items.clear() | ||
} | ||
} | ||
|
||
companion object { | ||
fun <T : Any> create() = AccumulatorSubject<T>() | ||
} | ||
} |
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
116 changes: 116 additions & 0 deletions
116
binder/src/test/java/com/badoo/binder/AccumulatorSubjectTest.kt
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,116 @@ | ||
package com.badoo.binder | ||
|
||
import com.badoo.binder.lifecycle.Lifecycle | ||
import com.badoo.binder.lifecycle.ManualLifecycle | ||
import io.reactivex.functions.Consumer | ||
import io.reactivex.subjects.PublishSubject | ||
import org.junit.jupiter.api.Test | ||
|
||
class AccumulatorSubjectTest { | ||
|
||
@Test | ||
fun `GIVEN the producer is an accumulator AND the consumer subscribes after the lifecycle started AND before the drain THEN consumes all the events produced`() { | ||
var score = 0 | ||
val lifecycle: ManualLifecycle = Lifecycle.manual() | ||
val producer = AccumulatorSubject.create<Int>() | ||
val consumer = PublishSubject.create<Int>() | ||
val testObserver = consumer.test() | ||
val binder = Binder(lifecycle) | ||
|
||
producer.accept(score++) | ||
producer.accept(score++) | ||
lifecycle.begin() | ||
binder.bind(producer to Consumer { consumer.onNext(it) }) | ||
binder.drain() | ||
|
||
testObserver.onComplete() | ||
testObserver.assertValues(0, 1) | ||
} | ||
|
||
@Test | ||
fun `GIVEN the producer is an accumulator AND the consumer subscribes after lifecycle started AND before the drain AND producer produces an event after the drain THEN consumes all the produced events`() { | ||
var score = 0 | ||
val lifecycle: ManualLifecycle = Lifecycle.manual() | ||
val producer = AccumulatorSubject.create<Int>() | ||
val consumer = PublishSubject.create<Int>() | ||
val testObserver = consumer.test() | ||
val binder = Binder(lifecycle) | ||
|
||
producer.accept(score++) | ||
producer.accept(score++) | ||
lifecycle.begin() | ||
binder.bind(producer to Consumer { consumer.onNext(it) }) | ||
binder.drain() | ||
producer.accept(score++) | ||
|
||
testObserver.onComplete() | ||
testObserver.assertValues(0, 1, 2) | ||
} | ||
|
||
@Test | ||
fun `GIVEN the producer is an accumulator AND the consumer subscribes after the lifecycle started AND after the drain THEN the consumer consumes only the events produced after the drain`() { | ||
var score = 0 | ||
val lifecycle: ManualLifecycle = Lifecycle.manual() | ||
val producer = AccumulatorSubject.create<Int>() | ||
val consumer = PublishSubject.create<Int>() | ||
val testObserver = consumer.test() | ||
val binder = Binder(lifecycle) | ||
|
||
producer.accept(score++) | ||
producer.accept(score++) | ||
lifecycle.begin() | ||
binder.drain() | ||
binder.bind(producer to Consumer { consumer.onNext(it) }) | ||
producer.accept(score++) | ||
|
||
testObserver.onComplete() | ||
testObserver.assertValues(2) | ||
} | ||
|
||
@Test | ||
fun `GIVEN the producer is an accumulator WHEN the consumer subscribes after the lifecycle started AND before the drain THEN should receive events produced before the drain and published when the lifecycle is active`() { | ||
var score = 0 | ||
val lifecycle: ManualLifecycle = Lifecycle.manual() | ||
val producer = AccumulatorSubject.create<Int>() | ||
val consumer = PublishSubject.create<Int>() | ||
val testObserver = consumer.test() | ||
val binder = Binder(lifecycle) | ||
|
||
producer.accept(score++) | ||
producer.accept(score++) | ||
lifecycle.begin() | ||
binder.bind(producer to Consumer { consumer.onNext(it) }) | ||
binder.drain() | ||
lifecycle.end() | ||
producer.accept(score++) | ||
lifecycle.begin() | ||
producer.accept(score++) | ||
|
||
testObserver.onComplete() | ||
testObserver.assertValues(0, 1, 3) | ||
} | ||
|
||
@Test | ||
fun `GIVEN the producer is an accumulator WHEN the consumer subscribes after the lifecycle restarted THEN should receive only the events after the restart when the lifecycle is active`() { | ||
var score = 0 | ||
val lifecycle: ManualLifecycle = Lifecycle.manual() | ||
val producer = AccumulatorSubject.create<Int>() | ||
val consumer = PublishSubject.create<Int>() | ||
val testObserver = consumer.test() | ||
val binder = Binder(lifecycle) | ||
|
||
producer.accept(score++) | ||
producer.accept(score++) | ||
lifecycle.begin() | ||
binder.drain() | ||
lifecycle.end() | ||
binder.bind(producer to Consumer { consumer.onNext(it) }) | ||
producer.accept(score++) | ||
lifecycle.begin() | ||
producer.accept(score++) | ||
|
||
testObserver.onComplete() | ||
testObserver.assertValues(3) | ||
} | ||
|
||
} |
Oops, something went wrong.