Skip to content

Commit

Permalink
Fast integration test for artificial driver (#6799)
Browse files Browse the repository at this point in the history
  • Loading branch information
VysotskiVadim authored Jan 16, 2023
1 parent 2275707 commit 6fc4e70
Show file tree
Hide file tree
Showing 5 changed files with 23,578 additions and 20 deletions.
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 {
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",
routerOrigin = RouterOrigin.Custom()
).first()
}
Loading

0 comments on commit 6fc4e70

Please sign in to comment.