Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NAVAND-1073: fix alternative routes refresh #6857

Merged
merged 5 commits into from
Jan 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/unreleased/bugfixes/6857.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Fixed an issue where alternative routes might have had an incorrect or incomplete portion of the route refreshed or occasionally fail to refresh.
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.mapbox.navigation.instrumentation_tests.core

import android.content.Context
import android.location.Location
import androidx.annotation.IntegerRes
import com.mapbox.api.directions.v5.DirectionsCriteria
import com.mapbox.api.directions.v5.models.Closure
import com.mapbox.api.directions.v5.models.DirectionsResponse
import com.mapbox.api.directions.v5.models.Incident
import com.mapbox.api.directions.v5.models.RouteOptions
import com.mapbox.geojson.Point
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
import com.mapbox.navigation.base.extensions.applyDefaultNavigationOptions
import com.mapbox.navigation.base.options.NavigationOptions
import com.mapbox.navigation.base.options.RoutingTilesOptions
Expand All @@ -26,6 +29,7 @@ import com.mapbox.navigation.instrumentation_tests.utils.coroutines.roadObjectsO
import com.mapbox.navigation.instrumentation_tests.utils.coroutines.routeProgressUpdates
import com.mapbox.navigation.instrumentation_tests.utils.coroutines.routesUpdates
import com.mapbox.navigation.instrumentation_tests.utils.coroutines.sdkTest
import com.mapbox.navigation.instrumentation_tests.utils.coroutines.setNavigationRoutesAndWaitForAlternativesUpdate
import com.mapbox.navigation.instrumentation_tests.utils.coroutines.setNavigationRoutesAndWaitForUpdate
import com.mapbox.navigation.instrumentation_tests.utils.http.FailByRequestMockRequestHandler
import com.mapbox.navigation.instrumentation_tests.utils.http.MockDirectionsRefreshHandler
Expand All @@ -34,6 +38,8 @@ import com.mapbox.navigation.instrumentation_tests.utils.http.MockRoutingTileEnd
import com.mapbox.navigation.instrumentation_tests.utils.idling.IdlingPolicyTimeoutRule
import com.mapbox.navigation.instrumentation_tests.utils.location.MockLocationReplayerRule
import com.mapbox.navigation.instrumentation_tests.utils.readRawFileText
import com.mapbox.navigation.instrumentation_tests.utils.routes.MockRoute
import com.mapbox.navigation.instrumentation_tests.utils.routes.RoutesProvider.toNavigationRoutes
import com.mapbox.navigation.testing.ui.BaseTest
import com.mapbox.navigation.testing.ui.utils.getMapboxAccessTokenFromResources
import com.mapbox.navigation.testing.ui.utils.runOnMainSync
Expand All @@ -54,6 +60,7 @@ import java.net.URI
import java.util.concurrent.TimeUnit
import kotlin.math.absoluteValue

@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
class RouteRefreshTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::class.java) {

@get:Rule
Expand All @@ -80,6 +87,11 @@ class RouteRefreshTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::class.ja
Point.fromLngLat(-75.525486, 38.772959),
Point.fromLngLat(-74.698765, 39.822911)
)
private val multilegCoordinates = listOf(
Point.fromLngLat(38.577764, -121.496066),
Point.fromLngLat(38.576795, -121.480256),
Point.fromLngLat(38.582195, -121.468458)
)

private lateinit var failByRequestRouteRefreshResponse: FailByRequestMockRequestHandler

Expand Down Expand Up @@ -424,7 +436,7 @@ class RouteRefreshTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::class.ja
R.raw.route_response_route_refresh,
R.raw.route_response_route_refresh_truncated_first_leg,
"route_response_route_refresh",
acceptedGeometryIndex = 5
acceptedGeometryIndex = 3
)
val routeOptions = generateRouteOptions(twoCoordinates)
val requestedRoutes = mapboxNavigation.requestRoutes(routeOptions)
Expand All @@ -433,10 +445,10 @@ class RouteRefreshTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::class.ja

mapboxNavigation.setNavigationRoutes(requestedRoutes)
mapboxNavigation.startTripSession()
// corresponds to currentRouteGeometryIndex = 5
stayOnPosition(38.57622, -121.496731)
// corresponds to currentRouteGeometryIndex = 3
stayOnPosition(38.577344, -121.496248)
mapboxNavigation.routeProgressUpdates()
.filter { it.currentRouteGeometryIndex == 5 }
.filter { it.currentRouteGeometryIndex == 3 }
.first()
val refreshedRoutes = mapboxNavigation.routesUpdates()
.filter {
Expand All @@ -446,10 +458,151 @@ class RouteRefreshTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::class.ja
.navigationRoutes

assertEquals(224.224, requestedRoutes[0].getSumOfDurationAnnotationsFromLeg(0), 0.0001)
assertEquals(169.582, refreshedRoutes[0].getSumOfDurationAnnotationsFromLeg(0), 0.0001)
assertEquals(172.175, refreshedRoutes[0].getSumOfDurationAnnotationsFromLeg(0), 0.0001)

assertEquals(227.918, requestedRoutes[1].getSumOfDurationAnnotationsFromLeg(0), 0.0001)
assertEquals(234.024, refreshedRoutes[1].getSumOfDurationAnnotationsFromLeg(0), 0.0001)
assertEquals(235.641, refreshedRoutes[1].getSumOfDurationAnnotationsFromLeg(0), 0.0001)
}

@Test
fun route_refresh_updates_annotations_for_new_alternative_with_different_number_of_legs() =
sdkTest {
setupMockRequestHandlers(
multilegCoordinates,
R.raw.route_response_single_route_multileg,
R.raw.route_response_single_route_multileg_refreshed,
"route_response_single_route_multileg",
acceptedGeometryIndex = 70
)
mockWebServerRule.requestHandlers.add(
FailByRequestMockRequestHandler(
MockDirectionsRefreshHandler(
"route_response_single_route_multileg_alternative",
readRawFileText(
activity,
R.raw.route_response_single_route_multileg_alternative_refreshed
),
acceptedGeometryIndex = 11
)
)
)
val routeOptions = generateRouteOptions(multilegCoordinates)
val requestedRoutes = mapboxNavigation.requestRoutes(routeOptions)
.getSuccessfulResultOrThrowException()
.routes
// alternative which was requested on the second leg of the original route,
// so the alternative has only one leg while the original route has two
val alternativeRoute = alternativeForMultileg(activity).toNavigationRoutes().first()

mapboxNavigation.setNavigationRoutes(requestedRoutes, initialLegIndex = 1)
mapboxNavigation.startTripSession()

// corresponds to currentRouteGeometryIndex = 70 for primary route and 11 for alternative route
stayOnPosition(38.581798, -121.476146)
mapboxNavigation.routeProgressUpdates()
.filter {
it.currentRouteGeometryIndex == 70
}
.first()

mapboxNavigation.setNavigationRoutesAndWaitForAlternativesUpdate(
requestedRoutes + alternativeRoute,
initialLegIndex = 1
)

val refreshedRoutes = mapboxNavigation.routesUpdates()
.filter {
it.reason == ROUTES_UPDATE_REASON_REFRESH
}
.first()
.navigationRoutes

assertEquals(
requestedRoutes[0].getSumOfDurationAnnotationsFromLeg(0),
refreshedRoutes[0].getSumOfDurationAnnotationsFromLeg(0),
0.0001
)

assertEquals(201.673, requestedRoutes[0].getSumOfDurationAnnotationsFromLeg(1), 0.0001)
assertEquals(202.881, refreshedRoutes[0].getSumOfDurationAnnotationsFromLeg(1), 0.0001)

assertEquals(194.3, alternativeRoute.getSumOfDurationAnnotationsFromLeg(0), 0.0001)
assertEquals(187.126, refreshedRoutes[1].getSumOfDurationAnnotationsFromLeg(0), 0.0001)
}

@Test
fun route_refresh_updates_annotations_for_new_alternative_with_more_legs() =
sdkTest {
setupMockRequestHandlers(
multilegCoordinates,
R.raw.route_response_single_route_multileg,
R.raw.route_response_single_route_multileg_refreshed,
"route_response_single_route_multileg",
acceptedGeometryIndex = 70
)
mockWebServerRule.requestHandlers.add(
FailByRequestMockRequestHandler(
MockDirectionsRefreshHandler(
"route_response_single_route_multileg_alternative",
readRawFileText(
activity,
R.raw.route_response_single_route_multileg_alternative_refreshed
),
acceptedGeometryIndex = 11
)
)
)
val routeOptions = generateRouteOptions(multilegCoordinates)
val alternativeRoutes = mapboxNavigation.requestRoutes(routeOptions)
.getSuccessfulResultOrThrowException()
.routes
// In this test setup we are considering a case where user was driving along the route,
// started the second leg and received an alternative, and selected it before the fork.
// This means that the primary route is shorter than the alternative route (former primary route).
val primaryRoute = alternativeForMultileg(activity).toNavigationRoutes().first()

// corresponds to currentRouteGeometryIndex = 70 for alternative route and 11 for the primary route
mockLocationUpdatesRule.pushLocationUpdate(
mockLocationUpdatesRule.generateLocationUpdate {
latitude = 38.581798
longitude = -121.476146
}
)

mapboxNavigation.setNavigationRoutes(
listOf(primaryRoute) + alternativeRoutes,
initialLegIndex = 0
)
mapboxNavigation.startTripSession()

mapboxNavigation.routeProgressUpdates()
.filter {
it.currentRouteGeometryIndex == 11
}
.first()

val refreshedRoutes = mapboxNavigation.routesUpdates()
.filter {
it.reason == ROUTES_UPDATE_REASON_REFRESH
}
.first()
.navigationRoutes

assertEquals(
alternativeRoutes[0].getSumOfDurationAnnotationsFromLeg(0),
refreshedRoutes[1].getSumOfDurationAnnotationsFromLeg(0),
0.0001
)

assertEquals(
201.673,
alternativeRoutes[0].getSumOfDurationAnnotationsFromLeg(1),
0.0001
)
assertEquals(202.881, refreshedRoutes[1].getSumOfDurationAnnotationsFromLeg(1), 0.0001)

assertEquals(194.3, primaryRoute.getSumOfDurationAnnotationsFromLeg(0), 0.0001)
assertEquals(187.126, refreshedRoutes[0].getSumOfDurationAnnotationsFromLeg(0), 0.0001)
}

@Test
Expand Down Expand Up @@ -714,6 +867,30 @@ class RouteRefreshTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::class.ja
)
)
}

private fun alternativeForMultileg(context: Context): MockRoute {
val jsonResponse = readRawFileText(
context,
R.raw.route_response_single_route_multileg_alternative
)
val coordinates = listOf(
Point.fromLngLat(38.577427, -121.478077),
Point.fromLngLat(38.582195, -121.468458)
)
return MockRoute(
jsonResponse,
DirectionsResponse.fromJson(jsonResponse),
listOf(
MockDirectionsRequestHandler(
profile = DirectionsCriteria.PROFILE_DRIVING_TRAFFIC,
jsonResponse = jsonResponse,
expectedCoordinates = coordinates
)
),
coordinates,
emptyList()
)
}
}

private fun NavigationRoute.getSumOfDurationAnnotationsFromLeg(legIndex: Int): Double =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,11 @@ suspend fun MapboxNavigation.setNavigationRoutesAndAwaitError(
}

suspend fun MapboxNavigation.setNavigationRoutesAndWaitForAlternativesUpdate(
routes: List<NavigationRoute>
routes: List<NavigationRoute>,
initialLegIndex: Int = 0,
) =
withTimeout(MAX_TIME_TO_UPDATE_ROUTE) {
setNavigationRoutes(routes)
setNavigationRoutes(routes, initialLegIndex)
waitForAlternativeRoute()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@
{
"unknown": true
},
{
"unknown": true
},
{
"unknown": true
},
{
"speed": 40,
"unit": "km/h"
Expand Down Expand Up @@ -198,6 +204,8 @@
"unknown",
"unknown",
"unknown",
"unknown",
"unknown",
"low",
"low",
"low",
Expand All @@ -213,6 +221,8 @@
"low"
],
"speed": [
6.2,
6.3,
6.1,
5.8,
5.8,
Expand Down Expand Up @@ -249,6 +259,8 @@
9.7
],
"distance": [
10.2,
9.7,
24.5,
4,
4.6,
Expand Down Expand Up @@ -285,6 +297,8 @@
3.7
],
"duration": [
1.802,
3.002,
4.003,
0.687,
0.787,
Expand Down
Loading