Skip to content

Commit 077291b

Browse files
authored
Alternatives: adopt new NN handling alternatives route flow (#6214)
1 parent 0e32735 commit 077291b

File tree

9 files changed

+5640
-85
lines changed

9 files changed

+5640
-85
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,6 @@ This release depends on, and has been tested with, the following Mapbox dependen
340340
- Mapbox Android Core `v5.0.2` ([release notes](https://github.com/mapbox/mapbox-events-android/releases/tag/core-5.0.2))
341341
- Mapbox Android Telemetry `v8.1.5` ([release notes](https://github.com/mapbox/mapbox-events-android/releases/tag/telem-8.1.5-core-5.0.2))
342342

343-
344343
## Mapbox Navigation SDK 2.8.0-beta.1 - 25 August, 2022
345344
### Changelog
346345
[Changes between v2.8.0-alpha.3 and v2.8.0-beta.1](https://github.com/mapbox/mapbox-navigation-android/compare/v2.8.0-alpha.3...v2.8.0-beta.1)

instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/RouteAlternativesTest.kt

Lines changed: 183 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,20 @@ package com.mapbox.navigation.instrumentation_tests.core
22

33
import android.location.Location
44
import androidx.test.espresso.Espresso
5+
import com.mapbox.api.directions.v5.models.DirectionsResponse
56
import com.mapbox.api.directions.v5.models.DirectionsRoute
67
import com.mapbox.api.directions.v5.models.RouteOptions
78
import com.mapbox.geojson.Point
89
import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions
910
import com.mapbox.navigation.base.options.NavigationOptions
11+
import com.mapbox.navigation.base.route.NavigationRoute
1012
import com.mapbox.navigation.base.route.RouteAlternativesOptions
13+
import com.mapbox.navigation.base.route.RouterOrigin
14+
import com.mapbox.navigation.base.trip.model.RouteProgress
1115
import com.mapbox.navigation.core.MapboxNavigation
1216
import com.mapbox.navigation.core.MapboxNavigationProvider
17+
import com.mapbox.navigation.core.routealternatives.NavigationRouteAlternativesObserver
18+
import com.mapbox.navigation.core.routealternatives.RouteAlternativesError
1319
import com.mapbox.navigation.instrumentation_tests.R
1420
import com.mapbox.navigation.instrumentation_tests.activity.EmptyTestActivity
1521
import com.mapbox.navigation.instrumentation_tests.utils.MapboxNavigationRule
@@ -25,6 +31,8 @@ import com.mapbox.navigation.testing.ui.utils.getMapboxAccessTokenFromResources
2531
import com.mapbox.navigation.testing.ui.utils.runOnMainSync
2632
import com.mapbox.navigation.utils.internal.logE
2733
import org.junit.Assert.assertEquals
34+
import org.junit.Assert.assertFalse
35+
import org.junit.Assert.assertNotEquals
2836
import org.junit.Assert.assertNotNull
2937
import org.junit.Assert.assertTrue
3038
import 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>) {

instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/utils/idling/RouteAlternativesIdlingResource.kt

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.mapbox.navigation.core.MapboxNavigation
99
import com.mapbox.navigation.core.routealternatives.RouteAlternativesObserver
1010
import com.mapbox.navigation.testing.ui.idling.NavigationIdlingResource
1111
import com.mapbox.navigation.testing.ui.utils.runOnMainSync
12+
import junit.framework.Assert.assertNotNull
1213

1314
/**
1415
* Becomes idle when [RouteAlternativesObserver.onRouteAlternatives] is invoked,
@@ -17,16 +18,25 @@ import com.mapbox.navigation.testing.ui.utils.runOnMainSync
1718
*
1819
* If your test needs to receive the next set of alternatives, you must create a new
1920
* instance of [RouteAlternativesIdlingResource] to capture the next callback.
21+
* @param mapboxNavigation the instance of [MapboxNavigation]
22+
* @param onRoutesAlternatives lambda returns boolean value: true if alternatives
23+
* observer must keep working, false otherwise. If false is returned resource transiting for busy
24+
* to idling (see [IdlingResource.ResourceCallback.onTransitionToIdle]).
2025
*/
2126
class RouteAlternativesIdlingResource(
22-
val mapboxNavigation: MapboxNavigation
27+
val mapboxNavigation: MapboxNavigation,
28+
private val onRoutesAlternatives: (
29+
RouteProgress,
30+
List<DirectionsRoute>,
31+
RouterOrigin
32+
) -> Boolean = { _, _, _ -> true },
2333
) : NavigationIdlingResource(), RouteAlternativesObserver {
2434

2535
var routeProgress: RouteProgress? = null
2636
private set
2737
var alternatives: List<DirectionsRoute>? = null
2838
private set
29-
var calledOnMainThread = true
39+
var calledOnMainThread = false
3040

3141
private var callback: IdlingResource.ResourceCallback? = null
3242

@@ -52,14 +62,24 @@ class RouteAlternativesIdlingResource(
5262
alternatives: List<DirectionsRoute>,
5363
routerOrigin: RouterOrigin
5464
) {
65+
if (!onRoutesAlternatives.invoke(routeProgress, alternatives, routerOrigin)) {
66+
return
67+
}
68+
5569
// Verify this happens on the main thread.
56-
if (Looper.myLooper() != Looper.getMainLooper()) {
57-
calledOnMainThread = false
70+
if (Looper.myLooper() == Looper.getMainLooper()) {
71+
calledOnMainThread = true
5872
}
5973

6074
mapboxNavigation.unregisterRouteAlternativesObserver(this)
6175
this.routeProgress = routeProgress
6276
this.alternatives = alternatives
77+
6378
callback?.onTransitionToIdle()
6479
}
80+
81+
fun verifyOnRouteAlternativesAndProgressReceived() {
82+
assertNotNull(routeProgress)
83+
assertNotNull(alternatives)
84+
}
6585
}

0 commit comments

Comments
 (0)