@@ -2,14 +2,20 @@ package com.mapbox.navigation.instrumentation_tests.core
22
33import android.location.Location
44import androidx.test.espresso.Espresso
5+ import com.mapbox.api.directions.v5.models.DirectionsResponse
56import com.mapbox.api.directions.v5.models.DirectionsRoute
67import com.mapbox.api.directions.v5.models.RouteOptions
78import com.mapbox.geojson.Point
89import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions
910import com.mapbox.navigation.base.options.NavigationOptions
11+ import com.mapbox.navigation.base.route.NavigationRoute
1012import com.mapbox.navigation.base.route.RouteAlternativesOptions
13+ import com.mapbox.navigation.base.route.RouterOrigin
14+ import com.mapbox.navigation.base.trip.model.RouteProgress
1115import com.mapbox.navigation.core.MapboxNavigation
1216import com.mapbox.navigation.core.MapboxNavigationProvider
17+ import com.mapbox.navigation.core.routealternatives.NavigationRouteAlternativesObserver
18+ import com.mapbox.navigation.core.routealternatives.RouteAlternativesError
1319import com.mapbox.navigation.instrumentation_tests.R
1420import com.mapbox.navigation.instrumentation_tests.activity.EmptyTestActivity
1521import com.mapbox.navigation.instrumentation_tests.utils.MapboxNavigationRule
@@ -25,6 +31,8 @@ import com.mapbox.navigation.testing.ui.utils.getMapboxAccessTokenFromResources
2531import com.mapbox.navigation.testing.ui.utils.runOnMainSync
2632import com.mapbox.navigation.utils.internal.logE
2733import org.junit.Assert.assertEquals
34+ import org.junit.Assert.assertFalse
35+ import org.junit.Assert.assertNotEquals
2836import org.junit.Assert.assertNotNull
2937import org.junit.Assert.assertTrue
3038import org.junit.Before
@@ -54,6 +62,10 @@ class RouteAlternativesTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::cla
5462 Point .fromLngLat(- 122.2647245 , 37.8138895 )
5563 )
5664
65+ private companion object {
66+ private const val LOG_CATEGORY = " RouteAlternativesTest"
67+ }
68+
5769 override fun setupMockLocation (): Location = mockLocationUpdatesRule.generateLocationUpdate {
5870 latitude = coordinates[0 ].latitude()
5971 longitude = coordinates[0 ].longitude()
@@ -81,7 +93,7 @@ class RouteAlternativesTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::cla
8193 setupMockRequestHandlers(coordinates)
8294 val routes = requestDirectionsRouteSync(coordinates)
8395
84- // Play the slower alternative route.
96+ // Play primary route
8597 runOnMainSync {
8698 mapboxNavigation.historyRecorder.startRecording()
8799 mockLocationReplayerRule.playRoute(routes.first())
@@ -107,7 +119,61 @@ class RouteAlternativesTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::cla
107119 val countDownLatch = CountDownLatch (1 )
108120 runOnMainSync {
109121 mapboxNavigation.historyRecorder.stopRecording {
110- logE(" history path=$it " , " DEBUG" )
122+ logE(" history path=$it " , LOG_CATEGORY )
123+ countDownLatch.countDown()
124+ }
125+ }
126+ countDownLatch.await()
127+
128+ // Verify alternative routes events were triggered.
129+ assertEquals(2 , routes.size)
130+ assertTrue(mapboxNavigation.getNavigationRoutes().isNotEmpty())
131+ firstAlternative.verifyOnRouteAlternativesAndProgressReceived()
132+ firstAlternative.alternatives!! .forEach {
133+ assertFalse(routes.contains(it))
134+ }
135+ }
136+
137+ @Test
138+ fun alternative_routes_observer_is_not_triggered_for_set_routes () {
139+ // Prepare with alternative routes.
140+ setupMockRequestHandlers(coordinates)
141+ val routes = requestDirectionsRouteSync(coordinates)
142+
143+ // Play primary route
144+ runOnMainSync {
145+ mapboxNavigation.historyRecorder.startRecording()
146+ mockLocationReplayerRule.playRoute(routes.first())
147+ mapboxNavigation.startTripSession()
148+ }
149+
150+ // Wait for enhanced locations to start and then set the routes.
151+ val firstLocationIdlingResource = FirstLocationIdlingResource (mapboxNavigation)
152+ firstLocationIdlingResource.firstLocationSync()
153+
154+ // Subscribing for alternatives
155+ val firstAlternative = RouteAlternativesIdlingResource (
156+ mapboxNavigation
157+ ) { _, alternatives, _ ->
158+ alternatives.isNotEmpty()
159+ }
160+ firstAlternative.register()
161+
162+ runOnMainSync {
163+ mapboxNavigation.setRoutes(routes)
164+ }
165+
166+ mapboxHistoryTestRule.stopRecordingOnCrash(" alternatives failed" ) {
167+ Espresso .onIdle()
168+ }
169+ firstAlternative.unregister()
170+
171+ assertTrue(firstAlternative.calledOnMainThread)
172+
173+ val countDownLatch = CountDownLatch (1 )
174+ runOnMainSync {
175+ mapboxNavigation.historyRecorder.stopRecording {
176+ logE(" history path=$it " , LOG_CATEGORY )
111177 countDownLatch.countDown()
112178 }
113179 }
@@ -116,8 +182,122 @@ class RouteAlternativesTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::cla
116182 // Verify alternative routes events were triggered.
117183 assertEquals(2 , routes.size)
118184 assertTrue(mapboxNavigation.getRoutes().isNotEmpty())
119- assertNotNull(firstAlternative.routeProgress)
185+ firstAlternative.verifyOnRouteAlternativesAndProgressReceived()
186+
187+ assertNotEquals(
188+ " Verify setRoutes and alternatives are not the same routes" ,
189+ routes.drop(1 ).sortedBy { it.hashCode() }, // drop primary route
190+ firstAlternative.alternatives!! .sortedBy { it.hashCode() }
191+ )
192+ }
193+
194+ /* *
195+ * The test verifies that if set routes to Navigation that are come from alternatives + n
196+ * (where n >= 1) external routes, alternatives routes observer is not triggered with these routes.
197+ * That was happening in legacy versions of NN.
198+ */
199+ @Test
200+ fun additional_alternative_is_not_force_to_invoke_alternatives_observer () {
201+ // Prepare with alternative routes.
202+ setupMockRequestHandlers(coordinates)
203+ val routes = requestDirectionsRouteSync(coordinates)
204+
205+ // Play primary route
206+ runOnMainSync {
207+ mapboxNavigation.historyRecorder.startRecording()
208+ mockLocationReplayerRule.playRoute(routes.first())
209+ mapboxNavigation.startTripSession()
210+ }
211+
212+ // Wait for enhanced locations to start and then set the routes.
213+ val firstLocationIdlingResource = FirstLocationIdlingResource (mapboxNavigation)
214+ firstLocationIdlingResource.firstLocationSync()
215+ runOnMainSync {
216+ // infinity subscription to avoid triggering NN on every new observer
217+ mapboxNavigation.registerRouteAlternativesObserver(
218+ object : NavigationRouteAlternativesObserver {
219+ override fun onRouteAlternatives (
220+ routeProgress : RouteProgress ,
221+ alternatives : List <NavigationRoute >,
222+ routerOrigin : RouterOrigin
223+ ) = Unit
224+
225+ override fun onRouteAlternativesError (error : RouteAlternativesError ) = Unit
226+ }
227+ )
228+ mapboxNavigation.setRoutes(routes)
229+ }
230+
231+ // Subscribing for alternatives
232+ val firstAlternative = RouteAlternativesIdlingResource (
233+ mapboxNavigation
234+ ) { _, alternatives, _ ->
235+ alternatives.isNotEmpty()
236+ }
237+ firstAlternative.register()
238+ mapboxHistoryTestRule.stopRecordingOnCrash(" alternatives failed" ) {
239+ Espresso .onIdle()
240+ }
241+ firstAlternative.unregister()
242+
120243 assertNotNull(firstAlternative.alternatives)
244+
245+ val nextAlternativeObserver = RouteAlternativesIdlingResource (
246+ mapboxNavigation
247+ ) { _, alternatives, _ ->
248+ alternatives.isNotEmpty()
249+ }
250+ nextAlternativeObserver.register()
251+
252+ val externalAlternatives = DirectionsResponse .fromJson(
253+ readRawFileText(activity, R .raw.route_response_alternative_continue)
254+ ).routes().also {
255+ assertEquals(1 , it.size)
256+ }
257+
258+ lateinit var setRoutes: List <DirectionsRoute >
259+ runOnMainSync {
260+ setRoutes = (
261+ mutableListOf (
262+ mapboxNavigation.getRoutes().first()
263+ ) + firstAlternative.alternatives!! + externalAlternatives
264+ ).also {
265+ assertTrue(
266+ " Primary route + >=1 alternatives + external alternatives" ,
267+ it.size >= 3
268+ )
269+ }
270+ mapboxNavigation.setRoutes(setRoutes)
271+ }
272+
273+ mapboxHistoryTestRule.stopRecordingOnCrash(" next alternatives failed" ) {
274+ Espresso .onIdle()
275+ }
276+ nextAlternativeObserver.unregister()
277+
278+ val countDownLatch = CountDownLatch (1 )
279+ runOnMainSync {
280+ mapboxNavigation.historyRecorder.stopRecording {
281+ logE(" history path=$it " , LOG_CATEGORY )
282+ countDownLatch.countDown()
283+ }
284+ }
285+ countDownLatch.await()
286+
287+ // Verify alternative routes events were triggered.
288+ firstAlternative.verifyOnRouteAlternativesAndProgressReceived()
289+
290+ // Verify next alternative routes events were triggered
291+ nextAlternativeObserver.verifyOnRouteAlternativesAndProgressReceived()
292+
293+ // verify that set routes with alternatives + 1 external alternatives is not triggered
294+ // alternative observer
295+ assertNotEquals(
296+ " Alternative are not the same as setRoutes (alternatives might have " +
297+ " additional routes or remove one or a few but not equal)" ,
298+ setRoutes.drop(1 ).sortedBy { it.hashCode() }, // drop primary route
299+ nextAlternativeObserver.alternatives!! .sortedBy { it.hashCode() }
300+ )
121301 }
122302
123303 private fun setupMockRequestHandlers (coordinates : List <Point >) {
0 commit comments