-
Notifications
You must be signed in to change notification settings - Fork 320
Fast integration test for artificial driver #6799
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
Changes from all commits
4a306fa
c87ab6f
ce46fff
269f7b5
8e12f4a
6701931
d9471d9
428cd54
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package com.mapbox.navigation.core.trip.service | ||
|
||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import androidx.test.platform.app.InstrumentationRegistry | ||
import com.mapbox.navigation.base.options.NavigationOptions | ||
import com.mapbox.navigation.base.route.NavigationRoute | ||
import com.mapbox.navigation.base.route.RouterOrigin | ||
import com.mapbox.navigation.core.MapboxNavigation | ||
import com.mapbox.navigation.core.MapboxNavigationProvider | ||
import com.mapbox.navigation.core.navigator.toFixLocation | ||
import com.mapbox.navigation.core.replay.history.ReplayEventUpdateLocation | ||
import com.mapbox.navigation.core.replay.history.mapToLocation | ||
import com.mapbox.navigation.core.replay.route.ReplayRouteMapper | ||
import com.mapbox.navigation.core.test.R | ||
import com.mapbox.navigation.navigator.internal.MapboxNativeNavigator | ||
import com.mapbox.navigation.navigator.internal.MapboxNativeNavigatorImpl | ||
import com.mapbox.navigator.NavigationStatus | ||
import com.mapbox.navigator.NavigationStatusOrigin | ||
import com.mapbox.navigator.NavigatorObserver | ||
import com.mapbox.navigator.RouteState | ||
import com.mapbox.navigator.SetRoutesReason | ||
import kotlinx.coroutines.Dispatchers | ||
import kotlinx.coroutines.ExperimentalCoroutinesApi | ||
import kotlinx.coroutines.async | ||
import kotlinx.coroutines.channels.awaitClose | ||
import kotlinx.coroutines.flow.Flow | ||
import kotlinx.coroutines.flow.callbackFlow | ||
import kotlinx.coroutines.flow.map | ||
import kotlinx.coroutines.flow.takeWhile | ||
import kotlinx.coroutines.flow.toList | ||
import kotlinx.coroutines.runBlocking | ||
import org.junit.Assert.assertTrue | ||
import org.junit.Ignore | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import kotlin.coroutines.resume | ||
import kotlin.coroutines.suspendCoroutine | ||
|
||
@RunWith(AndroidJUnit4::class) | ||
class ArtificialDriverTest { | ||
|
||
@Test | ||
@Ignore("test sometimes fails because of https://mapbox.atlassian.net/browse/NN-418") | ||
fun nativeNavigatorFollowsArtificialDriverWithoutReroutes() = | ||
runBlocking<Unit>(Dispatchers.Main) { | ||
withNavigators { mapboxNavigation, nativeNavigator -> | ||
mapboxNavigation.historyRecorder.startRecording() | ||
val testRoute = getTestRoute() | ||
val events = createArtificialLocationUpdates(testRoute) | ||
val setRoutesResult = | ||
nativeNavigator.setRoutes(testRoute, reason = SetRoutesReason.NEW_ROUTE) | ||
assertTrue("result is $setRoutesResult", setRoutesResult.isValue) | ||
val statusesTracking = async<List<NavigationStatus>> { | ||
nativeNavigator.collectStatuses(untilRouteState = RouteState.COMPLETE) | ||
} | ||
|
||
for (location in events.map { it.location.mapToLocation() }) { | ||
assertTrue(nativeNavigator.updateLocation(location.toFixLocation())) | ||
} | ||
|
||
val states = statusesTracking.await() | ||
val historyFile = suspendCoroutine<String> { continuation -> | ||
mapboxNavigation.historyRecorder.stopRecording { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a suspend version of this method:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please help me find it? I don't see suspend version here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You probably meant this one, but artificial driver test is in a different module as it accesses internal APIs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this one. Oh, it's a different module. OK then. |
||
continuation.resume(it ?: "null") | ||
} | ||
} | ||
val offRouteState = states.filter { it.routeState == RouteState.OFF_ROUTE } | ||
assertTrue( | ||
"${offRouteState.size} off-route states have been detected(" + | ||
"more info in $historyFile): $offRouteState", | ||
offRouteState.isEmpty() | ||
) | ||
} | ||
} | ||
} | ||
|
||
private fun createArtificialLocationUpdates( | ||
testRoute: NavigationRoute | ||
): List<ReplayEventUpdateLocation> { | ||
val replayRouteMapper = ReplayRouteMapper() | ||
return replayRouteMapper | ||
.mapDirectionsRouteGeometry(testRoute.directionsRoute) | ||
.filterIsInstance<ReplayEventUpdateLocation>() | ||
} | ||
|
||
private suspend fun MapboxNativeNavigator.collectStatuses( | ||
untilRouteState: RouteState | ||
): MutableList<NavigationStatus> { | ||
val statues = mutableListOf<NavigationStatus>() | ||
statusUpdates() | ||
.map { it.status } | ||
.takeWhile { it.routeState != untilRouteState } | ||
.toList(statues) | ||
return statues | ||
} | ||
|
||
data class OnStatusUpdateParameters( | ||
val origin: NavigationStatusOrigin, | ||
val status: NavigationStatus | ||
) | ||
|
||
@OptIn(ExperimentalCoroutinesApi::class) | ||
fun MapboxNativeNavigator.statusUpdates(): Flow<OnStatusUpdateParameters> { | ||
return callbackFlow { | ||
val observer = NavigatorObserver { origin, status -> | ||
this.trySend(OnStatusUpdateParameters(origin, status)) | ||
} | ||
addNavigatorObserver(observer) | ||
awaitClose { | ||
removeNavigatorObserver(observer) | ||
} | ||
} | ||
} | ||
|
||
private suspend fun withNavigators( | ||
block: suspend (MapboxNavigation, MapboxNativeNavigator) -> Unit | ||
) { | ||
val context = InstrumentationRegistry.getInstrumentation().targetContext | ||
val mapboxNavigation = MapboxNavigationProvider.create( | ||
NavigationOptions.Builder(context) | ||
.accessToken(context.getString(R.string.mapbox_access_token)) | ||
.build() | ||
) | ||
try { | ||
block(mapboxNavigation, MapboxNativeNavigatorImpl) | ||
} finally { | ||
mapboxNavigation.onDestroy() | ||
} | ||
} | ||
|
||
private fun getTestRoute(): NavigationRoute { | ||
val context = InstrumentationRegistry.getInstrumentation().targetContext | ||
return NavigationRoute.create( | ||
directionsResponseJson = context.resources.openRawResource(R.raw.test_long_route) | ||
.readBytes().decodeToString(), | ||
routeRequestUrl = "https://api.mapbox.com/directions/v5/mapbox/driving/" + | ||
"11.566744%2C48.143769%3B8.675521%2C50.119087" + | ||
"?alternatives=false" + | ||
"&geometries=polyline6" + | ||
"&language=en" + | ||
"&overview=full" + | ||
"&steps=true" + | ||
"&access_token=YOUR_MAPBOX_ACCESS_TOKEN", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, you don't want it to be present in the URL. |
||
routerOrigin = RouterOrigin.Custom() | ||
).first() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@mapbox/navnative , we have already discussed it, but can you please confirm that this is valid usage of native navigator?
I generated location updates so that they looks like the regular ones, but I pass them to NN faster than they happen in reality keeping the original time, i.e. for the native navigator time goes faster than real time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like history recording replay, you can take the locations with proper monotonic time in them and feed back-to-back into the navigator - it will produce the correct result.