Skip to content

Commit

Permalink
add ability to handle all lifecycles
Browse files Browse the repository at this point in the history
  • Loading branch information
kmadsen committed Aug 25, 2022
1 parent 63c6a82 commit 9bf5554
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

/**
* Extension function to make it simple to create the [RequireMapboxNavigationDelegate]. Below are
* Extension function to make it simple to create the [RequireMapboxNavigationProperty]. Below are
* a couple examples of how you may use the delegate.
*
* Default can be used when [MapboxNavigationApp] is setup elsewhere.
Expand All @@ -22,10 +22,10 @@ import kotlin.reflect.KProperty
* }
* ```
*
* Initialize and setup subscriptions
* Register subscriptions and setup MapboxNavigationApp
* ```
* private val mapboxNavigation by requireMapboxNavigation(
* observer = object : MapboxNavigationObserver {
* onResumedObserver = object : MapboxNavigationObserver {
* override fun onAttached(mapboxNavigation: MapboxNavigation) {
* mapboxNavigation.registerLocationObserver(locationObserver)
* mapboxNavigation.registerRoutesObserver(routesObserver)
Expand All @@ -44,12 +44,19 @@ import kotlin.reflect.KProperty
* }
* ```
*
* @see [RequireMapboxNavigationDelegate] for more details.
* @see [RequireMapboxNavigationProperty] for more details.
*/
fun requireMapboxNavigation(
observer: MapboxNavigationObserver? = null,
onCreatedObserver: MapboxNavigationObserver? = null,
onStartedObserver: MapboxNavigationObserver? = null,
onResumedObserver: MapboxNavigationObserver? = null,
onInitialize: () -> Unit
) = RequireMapboxNavigationDelegate(observer, onInitialize)
) = RequireMapboxNavigationProperty(
onCreatedObserver = onCreatedObserver,
onStartedObserver = onStartedObserver,
onResumedObserver = onResumedObserver,
onInitialize = onInitialize
)

/**
* Creates a simple way to use the [MapboxNavigationApp] inside a [LifecycleOwner] and provides
Expand All @@ -59,37 +66,47 @@ fun requireMapboxNavigation(
* the onCreate calls, or any call that happens before this delegate is accessed. The delegate will
* crash if the app is not setup and an attached has been created.
*
* You can use the [observer] parameter to setup any subscriptions. This is important because the
* You can use the observers parameter to setup any subscriptions. This is important because the
* [MapboxNavigation] instance can be re-created with [MapboxNavigationApp.disable], or if all
* [MapboxNavigationApp.attach] lifecycles are destroyed.
*
* @param observer to setup any subscriptions to [MapboxNavigation]
* @param onCreatedObserver to setup any subscriptions to [MapboxNavigation]
* @param onStartedObserver to setup any subscriptions to [MapboxNavigation]
* @param onResumedObserver to setup any subscriptions to [MapboxNavigation]
* @param onInitialize called after the lifecycle has been attached
*/
class RequireMapboxNavigationDelegate(
private val observer: MapboxNavigationObserver? = null,
class RequireMapboxNavigationProperty(
private val onCreatedObserver: MapboxNavigationObserver? = null,
private val onStartedObserver: MapboxNavigationObserver? = null,
private val onResumedObserver: MapboxNavigationObserver? = null,
private val onInitialize: (() -> Unit)? = null
) : ReadOnlyProperty<LifecycleOwner, MapboxNavigation> {

private lateinit var lifecycleOwner: LifecycleOwner

private val mapboxNavigationObserver = object : MapboxNavigationObserver {
override fun onAttached(mapboxNavigation: MapboxNavigation) {
observer?.onAttached(mapboxNavigation)
private val lifecycleObserver = object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
onCreatedObserver?.let { MapboxNavigationApp.registerObserver(it) }
}

override fun onDetached(mapboxNavigation: MapboxNavigation) {
observer?.onDetached(mapboxNavigation)
override fun onDestroy(owner: LifecycleOwner) {
onCreatedObserver?.let { MapboxNavigationApp.unregisterObserver(it) }
}
}

private val lifecycleObserver = object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
MapboxNavigationApp.registerObserver(mapboxNavigationObserver)
override fun onStart(owner: LifecycleOwner) {
onStartedObserver?.let { MapboxNavigationApp.registerObserver(it) }
}

override fun onDestroy(owner: LifecycleOwner) {
MapboxNavigationApp.unregisterObserver(mapboxNavigationObserver)
override fun onStop(owner: LifecycleOwner) {
onStartedObserver?.let { MapboxNavigationApp.unregisterObserver(it) }
}

override fun onResume(owner: LifecycleOwner) {
onResumedObserver?.let { MapboxNavigationApp.registerObserver(it) }
}

override fun onPause(owner: LifecycleOwner) {
onResumedObserver?.let { MapboxNavigationApp.unregisterObserver(it) }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.unmockkAll
import io.mockk.verify
import io.mockk.verifyOrder
import org.junit.After
import org.junit.Assert.assertTrue
Expand All @@ -19,10 +20,12 @@ import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
class RequireMapboxNavigationDelegateTest {
class RequireMapboxNavigationPropertyTest {

private class SystemUnderTest(
observer: MapboxNavigationObserver? = null,
onCreatedObserver: MapboxNavigationObserver? = null,
onStartedObserver: MapboxNavigationObserver? = null,
onResumedObserver: MapboxNavigationObserver? = null,
onInitialize: (() -> Unit)? = null
) : LifecycleOwner {
private val lifecycleRegistry = LifecycleRegistry(this)
Expand All @@ -34,7 +37,12 @@ class RequireMapboxNavigationDelegateTest {
lifecycleRegistry.currentState = state
}

val mapboxNavigation by RequireMapboxNavigationDelegate(observer, onInitialize)
val mapboxNavigation by RequireMapboxNavigationProperty(
onCreatedObserver = onCreatedObserver,
onStartedObserver = onStartedObserver,
onResumedObserver = onResumedObserver,
onInitialize = onInitialize
)
}

@Before
Expand Down Expand Up @@ -141,30 +149,127 @@ class RequireMapboxNavigationDelegateTest {
}

@Test
fun `observer parameter will be notified of changes to MapboxNavigation`() {
fun `observer callback order of resetting MapboxNavigationApp`() {
mockMapboxNavigationAppBehavior()
val observer = mockk<MapboxNavigationObserver>(relaxed = true)
val sut = SystemUnderTest(observer = observer)
val onCreatedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val onStartedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val onResumedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val sut = SystemUnderTest(
onCreatedObserver = onCreatedObserver,
onStartedObserver = onStartedObserver,
onResumedObserver = onResumedObserver,
)

MapboxNavigationApp.setup(mockk<NavigationOptions>())
sut.moveToState(Lifecycle.State.CREATED)
sut.moveToState(Lifecycle.State.RESUMED)
val firstRef = sut.mapboxNavigation
MapboxNavigationApp.disable()
MapboxNavigationApp.setup(mockk<NavigationOptions>())
val secondRef = sut.mapboxNavigation

verifyOrder {
onCreatedObserver.onAttached(firstRef)
onStartedObserver.onAttached(firstRef)
onResumedObserver.onAttached(firstRef)
onCreatedObserver.onDetached(firstRef)
onStartedObserver.onDetached(firstRef)
onResumedObserver.onDetached(firstRef)
onCreatedObserver.onAttached(secondRef)
onStartedObserver.onAttached(secondRef)
onResumedObserver.onAttached(secondRef)
}
}

@Test
fun `CREATED lifecycle state callback order`() {
mockMapboxNavigationAppBehavior()
val onCreatedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val onStartedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val onResumedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val sut = SystemUnderTest(
onCreatedObserver = onCreatedObserver,
onStartedObserver = onStartedObserver,
onResumedObserver = onResumedObserver,
)

MapboxNavigationApp.setup(mockk<NavigationOptions>())
sut.moveToState(Lifecycle.State.CREATED)
val firstRef = sut.mapboxNavigation
sut.moveToState(Lifecycle.State.DESTROYED)

verifyOrder {
observer.onAttached(firstRef)
observer.onDetached(firstRef)
observer.onAttached(secondRef)
observer.onDetached(secondRef)
onCreatedObserver.onAttached(firstRef)
onCreatedObserver.onDetached(firstRef)
}
verify(exactly = 0) {
onStartedObserver.onDetached(any())
onStartedObserver.onDetached(any())
onResumedObserver.onDetached(any())
onResumedObserver.onDetached(any())
}
}

@Test
fun `STARTED lifecycle state callback order`() {
mockMapboxNavigationAppBehavior()
val onCreatedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val onStartedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val onResumedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val sut = SystemUnderTest(
onCreatedObserver = onCreatedObserver,
onStartedObserver = onStartedObserver,
onResumedObserver = onResumedObserver,
)

MapboxNavigationApp.setup(mockk<NavigationOptions>())
sut.moveToState(Lifecycle.State.STARTED)
val firstRef = sut.mapboxNavigation
sut.moveToState(Lifecycle.State.CREATED)

verifyOrder {
onCreatedObserver.onAttached(firstRef)
onStartedObserver.onAttached(firstRef)
onStartedObserver.onDetached(firstRef)
}
verify(exactly = 0) {
onCreatedObserver.onDetached(any())
onResumedObserver.onAttached(any())
onResumedObserver.onDetached(any())
}
}

@Test
fun `RESUMED lifecycle state callback order`() {
mockMapboxNavigationAppBehavior()
val onCreatedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val onStartedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val onResumedObserver = mockk<MapboxNavigationObserver>(relaxed = true)
val sut = SystemUnderTest(
onCreatedObserver = onCreatedObserver,
onStartedObserver = onStartedObserver,
onResumedObserver = onResumedObserver,
)

MapboxNavigationApp.setup(mockk<NavigationOptions>())
sut.moveToState(Lifecycle.State.RESUMED)
val firstRef = sut.mapboxNavigation
sut.moveToState(Lifecycle.State.STARTED)

verifyOrder {
onCreatedObserver.onAttached(firstRef)
onStartedObserver.onAttached(firstRef)
onResumedObserver.onAttached(firstRef)
onResumedObserver.onDetached(firstRef)
}
verify(exactly = 0) {
onCreatedObserver.onDetached(any())
onStartedObserver.onDetached(any())
}
}

/**
* This mocks the behavior of [MapboxNavigationApp] in order to showcase what is expected from
* the [RequireMapboxNavigationDelegate].
* the [RequireMapboxNavigationProperty].
*/
private fun mockMapboxNavigationAppBehavior() {
var mockMapboxNavigation: MapboxNavigation? = null
Expand Down

0 comments on commit 9bf5554

Please sign in to comment.