@@ -10,14 +10,19 @@ import com.mapbox.geojson.Point
1010import com.mapbox.geojson.utils.PolylineUtils
1111import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
1212import com.mapbox.navigation.base.options.NavigationOptions
13+ import com.mapbox.navigation.base.route.NavigationRoute
14+ import com.mapbox.navigation.base.trip.model.RouteProgress
1315import com.mapbox.navigation.core.MapboxNavigation
1416import com.mapbox.navigation.core.directions.session.RoutesObserver
1517import com.mapbox.navigation.core.directions.session.RoutesUpdatedResult
1618import com.mapbox.navigation.core.replay.MapboxReplayer
1719import com.mapbox.navigation.core.replay.history.ReplayEventBase
1820import com.mapbox.navigation.core.replay.history.ReplayEventUpdateLocation
1921import com.mapbox.navigation.core.replay.history.ReplayEventsObserver
22+ import com.mapbox.navigation.core.trip.session.RouteProgressObserver
2023import com.mapbox.navigation.testing.LoggingFrontendTestRule
24+ import com.mapbox.turf.TurfConstants
25+ import com.mapbox.turf.TurfMeasurement
2126import io.mockk.every
2227import io.mockk.just
2328import io.mockk.mockk
@@ -47,9 +52,13 @@ class ReplayRouteSessionTest {
4752 private val options: NavigationOptions = mockk {
4853 every { applicationContext } returns context
4954 }
55+ private val routesObserver = slot<RoutesObserver >()
56+ private val routeProgressObserver = slot<RouteProgressObserver >()
5057 private val mapboxNavigation: MapboxNavigation = mockk(relaxed = true ) {
5158 every { mapboxReplayer } returns replayer
5259 every { navigationOptions } returns options
60+ every { registerRoutesObserver(capture(routesObserver)) } just runs
61+ every { registerRouteProgressObserver(capture(routeProgressObserver)) } just runs
5362 }
5463 private val bestLocationEngine: LocationEngine = mockk {
5564 every { getLastLocation(any()) } just runs
@@ -166,16 +175,14 @@ class ReplayRouteSessionTest {
166175
167176 @Test
168177 fun `onAttached - should push the initial batch of events` () {
169- val routesObserver = slot<RoutesObserver >()
170- every { mapboxNavigation.registerRoutesObserver(capture(routesObserver)) } just runs
171178 sut.setOptions(
172179 ReplayRouteSessionOptions .Builder ()
173180 .decodeMinDistance(1.0 )
174181 .build()
175182 )
176183
177184 sut.onAttached(mapboxNavigation)
178- routesObserver .captured.onRoutesChanged(mockActiveNavigationRoutes( ))
185+ routeProgressObserver .captured.onRouteProgressChanged(mockRouteProgress( 0.0 ))
179186
180187 val pushedEvents = slot<List <ReplayEventBase >>()
181188 verify { replayer.pushEvents(capture(pushedEvents)) }
@@ -193,45 +200,34 @@ class ReplayRouteSessionTest {
193200 replayEventsObserver.captured.replayEvents(firstArg())
194201 replayer
195202 }
196- val routesUpdatedResult = mockActiveNavigationRoutes( )
203+ val routeProgress = mockRouteProgress( 0.0 )
197204
198205 sut.setOptions(
199206 ReplayRouteSessionOptions .Builder ()
200207 .decodeMinDistance(0.001 )
201208 .build()
202209 )
203210 sut.onAttached(mapboxNavigation)
204- routesObserver .captured.onRoutesChanged(routesUpdatedResult )
211+ routeProgressObserver .captured.onRouteProgressChanged(routeProgress )
205212
206213 // Verify every point in the geometry was simulated
207214 val pushedPoints = pushedEvents.flatten().toList().map {
208215 val location = (it as ReplayEventUpdateLocation ).location
209216 Point .fromLngLat(location.lon, location.lat)
210217 }
211- val geometry = routesUpdatedResult.navigationRoutes.first() .directionsRoute.geometry()!!
218+ val geometry = routeProgress.navigationRoute .directionsRoute.geometry()!!
212219 val geometryPoints = PolylineUtils .decode(geometry, 6 )
213- assertTrue(pushedPoints.size > geometryPoints.size)
220+ assertTrue(
221+ " ${pushedPoints.size} > ${geometryPoints.size} " ,
222+ pushedPoints.size > geometryPoints.size
223+ )
214224 assertTrue(
215225 geometryPoints.all { lhs ->
216226 pushedPoints.firstOrNull { rhs -> lhs.equals(rhs) } != null
217227 }
218228 )
219229 }
220230
221- @Test
222- fun `onAttached - should request gps location when resetLocationEnabled is true` () {
223- every { PermissionsManager .areLocationPermissionsGranted(any()) } returns true
224- sut.setOptions(ReplayRouteSessionOptions .Builder ().locationResetEnabled(true ).build())
225- sut.onAttached(mapboxNavigation)
226-
227- verifyOrder {
228- mapboxNavigation.stopTripSession()
229- mapboxNavigation.startReplayTripSession()
230- bestLocationEngine.getLastLocation(any())
231- replayer.play()
232- }
233- }
234-
235231 @Test
236232 fun `onAttached - should push gps location when route is not set` () {
237233 val locationCallbackSlot = slot<LocationEngineCallback <LocationEngineResult >>()
@@ -242,6 +238,9 @@ class ReplayRouteSessionTest {
242238
243239 sut.setOptions(ReplayRouteSessionOptions .Builder ().locationResetEnabled(true ).build())
244240 sut.onAttached(mapboxNavigation)
241+ routesObserver.captured.onRoutesChanged(
242+ mockk { every { navigationRoutes } returns emptyList() }
243+ )
245244 locationCallbackSlot.captured.onSuccess(
246245 mockk {
247246 every { lastLocation } returns mockk(relaxed = true ) {
@@ -263,21 +262,115 @@ class ReplayRouteSessionTest {
263262 assertEquals(- 2.0 , capturedLocation.location.lon, 0.0 )
264263 }
265264
266- private fun mockActiveNavigationRoutes (): RoutesUpdatedResult = mockk {
265+ @Test
266+ fun `onAttached registered listeners should be unregistered onDetached` () {
267+ val progressObserver = slot<RouteProgressObserver >()
268+ val routesObserver = slot<RoutesObserver >()
269+ val replayEventsObserver = slot<ReplayEventsObserver >()
270+ every { mapboxNavigation.registerRoutesObserver(capture(routesObserver)) } just runs
271+ every {
272+ mapboxNavigation.registerRouteProgressObserver(capture(progressObserver))
273+ } just runs
274+ every { replayer.registerObserver(capture(replayEventsObserver)) } just runs
275+
276+ sut.onAttached(mapboxNavigation)
277+ sut.onDetached(mapboxNavigation)
278+
279+ verifyOrder {
280+ mapboxNavigation.registerRouteProgressObserver(any())
281+ mapboxNavigation.registerRoutesObserver(any())
282+ replayer.registerObserver(any())
283+ mapboxNavigation.unregisterRoutesObserver(routesObserver.captured)
284+ mapboxNavigation.unregisterRouteProgressObserver(progressObserver.captured)
285+ replayer.unregisterObserver(replayEventsObserver.captured)
286+ }
287+ }
288+
289+ @Test
290+ fun `onAttached - should skip to the current routeProgress distanceTraveled` () {
291+ val progressObserver = slot<RouteProgressObserver >()
292+ val routesObserver = slot<RoutesObserver >()
293+ every {
294+ mapboxNavigation.registerRouteProgressObserver(capture(progressObserver))
295+ } just runs
296+ every { mapboxNavigation.registerRoutesObserver(capture(routesObserver)) } just runs
297+ val activeRoutes = mockActiveRoutesUpdatedResult()
298+ val primaryRoute = activeRoutes.navigationRoutes.first()
299+
300+ sut.onAttached(mapboxNavigation)
301+ progressObserver.captured.onRouteProgressChanged(
302+ mockk {
303+ every { navigationRoute } returns primaryRoute
304+ every { distanceTraveled } returns 100.0f
305+ }
306+ )
307+ routesObserver.captured.onRoutesChanged(activeRoutes)
308+
309+ val pushedEvents = slot<List <ReplayEventBase >>()
310+ verify { replayer.pushEvents(capture(pushedEvents)) }
311+ verifyDistanceTraveled(pushedEvents.captured, primaryRoute, 100.0 )
312+ }
313+
314+ @Test
315+ fun `onAttached - should skip to short routeProgress distanceTraveled` () {
316+ val progressObserver = slot<RouteProgressObserver >()
317+ val routesObserver = slot<RoutesObserver >()
318+ every {
319+ mapboxNavigation.registerRouteProgressObserver(capture(progressObserver))
320+ } just runs
321+ every { mapboxNavigation.registerRoutesObserver(capture(routesObserver)) } just runs
322+ val activeRoutes = mockActiveRoutesUpdatedResult()
323+ val primaryRoute = activeRoutes.navigationRoutes.first()
324+
325+ sut.onAttached(mapboxNavigation)
326+ progressObserver.captured.onRouteProgressChanged(
327+ mockk {
328+ every { navigationRoute } returns primaryRoute
329+ every { distanceTraveled } returns 40.0f
330+ }
331+ )
332+ routesObserver.captured.onRoutesChanged(activeRoutes)
333+
334+ val pushedEvents = slot<List <ReplayEventBase >>()
335+ verify { replayer.pushEvents(capture(pushedEvents)) }
336+ verifyDistanceTraveled(pushedEvents.captured, primaryRoute, 40.0 )
337+ }
338+
339+ private fun verifyDistanceTraveled (
340+ pushedEvents : List <ReplayEventBase >,
341+ primaryRoute : NavigationRoute ,
342+ distanceTraveled : Double
343+ ) {
344+ val geometry = primaryRoute.directionsRoute.geometry()!!
345+ val fullRoute = PolylineUtils .decode(geometry, 6 )
346+ val expected = TurfMeasurement .along(fullRoute, distanceTraveled, TurfConstants .UNIT_METERS )
347+ val firstReplayLocation = (pushedEvents.first() as ReplayEventUpdateLocation ).location
348+ val firstReplayPoint = Point .fromLngLat(firstReplayLocation.lon, firstReplayLocation.lat)
349+ val distanceToPoint = TurfMeasurement .distance(expected, firstReplayPoint)
350+
351+ assertTrue(" 1.0 > $distanceToPoint " , 1.0 > distanceToPoint)
352+ }
353+
354+ private fun mockActiveRoutesUpdatedResult (): RoutesUpdatedResult = mockk {
355+ every { navigationRoutes } returns listOf (mockNavigationRoute())
356+ }
357+
358+ private fun mockRouteProgress (traveled : Double ): RouteProgress = mockk {
359+ every { navigationRoute } returns mockNavigationRoute()
360+ every { distanceTraveled } returns traveled.toFloat()
361+ }
362+
363+ private fun mockNavigationRoute (): NavigationRoute = mockk {
267364 val geometry = " _kmbgAppafhFwXaOuC}ApAoEbNqe@jAaEhEcOtAwEdAoD`DaLnCiJ|M_e@`Je[rAyEnEgO" +
268365 " tGiUxByHlDjBp@^zKdG`Ah@`HtDx@d@rGlDl@\\ pAp@dAl@p@^nItEpQvJfAh@fDjB`D`Br@`@nKpFbDhB" +
269366 " ~KlGtDvBvAwE|EqPzFeSvHaXtA{ElAiE|@_D"
270- every { navigationRoutes } returns listOf (
271- mockk {
272- every { id } returns " test-navigation-route-id"
273- every { routeOptions }
274- every { directionsRoute } returns mockk {
275- every { routeOptions() } returns mockk {
276- every { geometries() } returns " polyline6"
277- every { geometry() } returns geometry
278- }
279- }
367+ every { id } returns " test-navigation-route-id"
368+ every { routeOptions }
369+ every { directionsRoute } returns mockk {
370+ every { routeOptions() } returns mockk {
371+ every { geometries() } returns " polyline6"
372+ every { geometry() } returns geometry
280373 }
281- )
374+ }
282375 }
283376}
0 commit comments