Skip to content

Commit 6fc4e70

Browse files
authored
Fast integration test for artificial driver (#6799)
1 parent 2275707 commit 6fc4e70

File tree

5 files changed

+23578
-20
lines changed

5 files changed

+23578
-20
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package com.mapbox.navigation.core.trip.service
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import androidx.test.platform.app.InstrumentationRegistry
5+
import com.mapbox.navigation.base.options.NavigationOptions
6+
import com.mapbox.navigation.base.route.NavigationRoute
7+
import com.mapbox.navigation.base.route.RouterOrigin
8+
import com.mapbox.navigation.core.MapboxNavigation
9+
import com.mapbox.navigation.core.MapboxNavigationProvider
10+
import com.mapbox.navigation.core.navigator.toFixLocation
11+
import com.mapbox.navigation.core.replay.history.ReplayEventUpdateLocation
12+
import com.mapbox.navigation.core.replay.history.mapToLocation
13+
import com.mapbox.navigation.core.replay.route.ReplayRouteMapper
14+
import com.mapbox.navigation.core.test.R
15+
import com.mapbox.navigation.navigator.internal.MapboxNativeNavigator
16+
import com.mapbox.navigation.navigator.internal.MapboxNativeNavigatorImpl
17+
import com.mapbox.navigator.NavigationStatus
18+
import com.mapbox.navigator.NavigationStatusOrigin
19+
import com.mapbox.navigator.NavigatorObserver
20+
import com.mapbox.navigator.RouteState
21+
import com.mapbox.navigator.SetRoutesReason
22+
import kotlinx.coroutines.Dispatchers
23+
import kotlinx.coroutines.ExperimentalCoroutinesApi
24+
import kotlinx.coroutines.async
25+
import kotlinx.coroutines.channels.awaitClose
26+
import kotlinx.coroutines.flow.Flow
27+
import kotlinx.coroutines.flow.callbackFlow
28+
import kotlinx.coroutines.flow.map
29+
import kotlinx.coroutines.flow.takeWhile
30+
import kotlinx.coroutines.flow.toList
31+
import kotlinx.coroutines.runBlocking
32+
import org.junit.Assert.assertTrue
33+
import org.junit.Ignore
34+
import org.junit.Test
35+
import org.junit.runner.RunWith
36+
import kotlin.coroutines.resume
37+
import kotlin.coroutines.suspendCoroutine
38+
39+
@RunWith(AndroidJUnit4::class)
40+
class ArtificialDriverTest {
41+
42+
@Test
43+
@Ignore("test sometimes fails because of https://mapbox.atlassian.net/browse/NN-418")
44+
fun nativeNavigatorFollowsArtificialDriverWithoutReroutes() =
45+
runBlocking<Unit>(Dispatchers.Main) {
46+
withNavigators { mapboxNavigation, nativeNavigator ->
47+
mapboxNavigation.historyRecorder.startRecording()
48+
val testRoute = getTestRoute()
49+
val events = createArtificialLocationUpdates(testRoute)
50+
val setRoutesResult =
51+
nativeNavigator.setRoutes(testRoute, reason = SetRoutesReason.NEW_ROUTE)
52+
assertTrue("result is $setRoutesResult", setRoutesResult.isValue)
53+
val statusesTracking = async<List<NavigationStatus>> {
54+
nativeNavigator.collectStatuses(untilRouteState = RouteState.COMPLETE)
55+
}
56+
57+
for (location in events.map { it.location.mapToLocation() }) {
58+
assertTrue(nativeNavigator.updateLocation(location.toFixLocation()))
59+
}
60+
61+
val states = statusesTracking.await()
62+
val historyFile = suspendCoroutine<String> { continuation ->
63+
mapboxNavigation.historyRecorder.stopRecording {
64+
continuation.resume(it ?: "null")
65+
}
66+
}
67+
val offRouteState = states.filter { it.routeState == RouteState.OFF_ROUTE }
68+
assertTrue(
69+
"${offRouteState.size} off-route states have been detected(" +
70+
"more info in $historyFile): $offRouteState",
71+
offRouteState.isEmpty()
72+
)
73+
}
74+
}
75+
}
76+
77+
private fun createArtificialLocationUpdates(
78+
testRoute: NavigationRoute
79+
): List<ReplayEventUpdateLocation> {
80+
val replayRouteMapper = ReplayRouteMapper()
81+
return replayRouteMapper
82+
.mapDirectionsRouteGeometry(testRoute.directionsRoute)
83+
.filterIsInstance<ReplayEventUpdateLocation>()
84+
}
85+
86+
private suspend fun MapboxNativeNavigator.collectStatuses(
87+
untilRouteState: RouteState
88+
): MutableList<NavigationStatus> {
89+
val statues = mutableListOf<NavigationStatus>()
90+
statusUpdates()
91+
.map { it.status }
92+
.takeWhile { it.routeState != untilRouteState }
93+
.toList(statues)
94+
return statues
95+
}
96+
97+
data class OnStatusUpdateParameters(
98+
val origin: NavigationStatusOrigin,
99+
val status: NavigationStatus
100+
)
101+
102+
@OptIn(ExperimentalCoroutinesApi::class)
103+
fun MapboxNativeNavigator.statusUpdates(): Flow<OnStatusUpdateParameters> {
104+
return callbackFlow {
105+
val observer = NavigatorObserver { origin, status ->
106+
this.trySend(OnStatusUpdateParameters(origin, status))
107+
}
108+
addNavigatorObserver(observer)
109+
awaitClose {
110+
removeNavigatorObserver(observer)
111+
}
112+
}
113+
}
114+
115+
private suspend fun withNavigators(
116+
block: suspend (MapboxNavigation, MapboxNativeNavigator) -> Unit
117+
) {
118+
val context = InstrumentationRegistry.getInstrumentation().targetContext
119+
val mapboxNavigation = MapboxNavigationProvider.create(
120+
NavigationOptions.Builder(context)
121+
.accessToken(context.getString(R.string.mapbox_access_token))
122+
.build()
123+
)
124+
try {
125+
block(mapboxNavigation, MapboxNativeNavigatorImpl)
126+
} finally {
127+
mapboxNavigation.onDestroy()
128+
}
129+
}
130+
131+
private fun getTestRoute(): NavigationRoute {
132+
val context = InstrumentationRegistry.getInstrumentation().targetContext
133+
return NavigationRoute.create(
134+
directionsResponseJson = context.resources.openRawResource(R.raw.test_long_route)
135+
.readBytes().decodeToString(),
136+
routeRequestUrl = "https://api.mapbox.com/directions/v5/mapbox/driving/" +
137+
"11.566744%2C48.143769%3B8.675521%2C50.119087" +
138+
"?alternatives=false" +
139+
"&geometries=polyline6" +
140+
"&language=en" +
141+
"&overview=full" +
142+
"&steps=true" +
143+
"&access_token=YOUR_MAPBOX_ACCESS_TOKEN",
144+
routerOrigin = RouterOrigin.Custom()
145+
).first()
146+
}

0 commit comments

Comments
 (0)