-
Notifications
You must be signed in to change notification settings - Fork 319
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
14 changed files
with
518 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
224 changes: 224 additions & 0 deletions
224
.../androidTest/java/com/mapbox/navigation/instrumentation_tests/core/TelemetryEventsTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
package com.mapbox.navigation.instrumentation_tests.core | ||
|
||
import android.content.Context | ||
import android.location.Location | ||
import androidx.test.platform.app.InstrumentationRegistry | ||
import com.mapbox.bindgen.Value | ||
import com.mapbox.common.SettingsServiceFactory | ||
import com.mapbox.common.SettingsServiceStorageType | ||
import com.mapbox.geojson.Point | ||
import com.mapbox.navigation.base.options.DeviceProfile | ||
import com.mapbox.navigation.base.options.NavigationOptions | ||
import com.mapbox.navigation.base.trip.model.RouteProgressState | ||
import com.mapbox.navigation.core.MapboxNavigation | ||
import com.mapbox.navigation.core.MapboxNavigationProvider | ||
import com.mapbox.navigation.core.telemetry.events.FeedbackEvent | ||
import com.mapbox.navigation.instrumentation_tests.activity.EmptyTestActivity | ||
import com.mapbox.navigation.instrumentation_tests.utils.events.EventsAccumulatorRule | ||
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventArrive | ||
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventBase | ||
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventDepart | ||
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventFeedback | ||
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventFreeDrive | ||
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventNavigationStateChanged | ||
import com.mapbox.navigation.instrumentation_tests.utils.events.verify.verifyEvents | ||
import com.mapbox.navigation.instrumentation_tests.utils.http.EventsRequestHandle | ||
import com.mapbox.navigation.instrumentation_tests.utils.location.MockLocationReplayerRule | ||
import com.mapbox.navigation.instrumentation_tests.utils.routes.MockRoute | ||
import com.mapbox.navigation.instrumentation_tests.utils.routes.RoutesProvider | ||
import com.mapbox.navigation.instrumentation_tests.utils.routes.RoutesProvider.toNavigationRoutes | ||
import com.mapbox.navigation.testing.ui.BaseTest | ||
import com.mapbox.navigation.testing.ui.utils.MapboxNavigationRule | ||
import com.mapbox.navigation.testing.ui.utils.coroutines.passed | ||
import com.mapbox.navigation.testing.ui.utils.coroutines.rawLocationUpdates | ||
import com.mapbox.navigation.testing.ui.utils.coroutines.routeProgressUpdates | ||
import com.mapbox.navigation.testing.ui.utils.coroutines.sdkTest | ||
import com.mapbox.navigation.testing.ui.utils.getMapboxAccessTokenFromResources | ||
import com.mapbox.navigation.testing.ui.utils.runOnMainSync | ||
import kotlinx.coroutines.delay | ||
import kotlinx.coroutines.flow.first | ||
import org.junit.Before | ||
import org.junit.Rule | ||
import org.junit.Test | ||
|
||
class TelemetryEventsTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::class.java) { | ||
|
||
@get:Rule | ||
val eventsAccumulatorRule = EventsAccumulatorRule( | ||
getMapboxAccessTokenFromResources(InstrumentationRegistry.getInstrumentation().targetContext) | ||
) | ||
|
||
@get:Rule | ||
val mockLocationReplayerRule = MockLocationReplayerRule(mockLocationUpdatesRule) | ||
|
||
@get:Rule | ||
val mapboxNavigationRule = MapboxNavigationRule() | ||
|
||
private val coordinates = listOf( | ||
Point.fromLngLat(-77.031991, 38.894721), | ||
Point.fromLngLat(-77.031991, 38.895433), | ||
Point.fromLngLat(-77.030923, 38.895433), | ||
) | ||
|
||
private lateinit var mapboxNavigation: MapboxNavigation | ||
private val eventsRequestHandle = EventsRequestHandle() | ||
|
||
private companion object { | ||
private const val LOG_CATEGORY = "TelemetryEventsTest" | ||
|
||
/* See mapbox_common_settings_internal.hpp in common repo */ | ||
private const val TELEMETRY_EVENTS_BASE_URL_PROPERTY = | ||
"com.mapbox.common.telemetry.internal.events_base_url_override" | ||
|
||
private fun createMapboxNavigation(context: Context): MapboxNavigation = | ||
MapboxNavigationProvider.create( | ||
NavigationOptions.Builder(context) | ||
.accessToken(getMapboxAccessTokenFromResources(context)) | ||
.deviceProfile( | ||
DeviceProfile.Builder() | ||
.customConfig( | ||
""" | ||
{ | ||
"features": { | ||
"useTelemetryNavigationEvents": true | ||
}, | ||
"telemetry": { | ||
"eventsPriority": "Immediate" | ||
} | ||
} | ||
""".trimIndent() | ||
) | ||
.build() | ||
) | ||
.build() | ||
) | ||
|
||
/** | ||
* Give the chance to send events: 5 attempts with 1 seconds delay | ||
*/ | ||
private suspend fun waitForEventsBeSent(expectedEvents: Int, current: () -> Int) { | ||
IntRange(0, 4).forEach { _ -> | ||
if (expectedEvents == current()) { | ||
return | ||
} else { | ||
delay(1_000) | ||
} | ||
} | ||
} | ||
|
||
private fun List<String>.prettyErrors(): String = joinToString(postfix = "\n") | ||
} | ||
|
||
override fun setupMockLocation(): Location = mockLocationUpdatesRule.generateLocationUpdate { | ||
latitude = coordinates[0].latitude() | ||
longitude = coordinates[0].longitude() | ||
} | ||
|
||
@Before | ||
fun setup() { | ||
runOnMainSync { | ||
// substitute telemetry base url | ||
SettingsServiceFactory.getInstance(SettingsServiceStorageType.NON_PERSISTENT).apply { | ||
set(TELEMETRY_EVENTS_BASE_URL_PROPERTY, Value(mockWebServerRule.baseUrl)) | ||
} | ||
|
||
mockWebServerRule.requestHandlers.add(eventsRequestHandle) | ||
} | ||
} | ||
|
||
@Test | ||
fun freeDrivePlain() = sdkTest { | ||
val dcShorRoute = RoutesProvider.dc_short_with_alternative(activity).apply { | ||
setRouteAsOriginLocation() | ||
} | ||
mapboxNavigation = createMapboxNavigation(activity) | ||
|
||
mapboxNavigation.startTripSession() | ||
mockLocationReplayerRule.playRoute(dcShorRoute.routeResponse.routes().first()) | ||
mapboxNavigation.rawLocationUpdates().passed(50.0).first() | ||
mapboxNavigation.stopTripSession() | ||
|
||
verifyResult( | ||
EventFreeDrive(EventFreeDrive.Type.Start), | ||
EventFreeDrive(EventFreeDrive.Type.Stop), | ||
) | ||
} | ||
|
||
@Test | ||
fun activeGuidancePlain() = sdkTest { | ||
val dcShortRoute = RoutesProvider.dc_short_with_alternative(activity).apply { | ||
setRouteAsOriginLocation() | ||
} | ||
mapboxNavigation = createMapboxNavigation(activity) | ||
mockLocationReplayerRule.playbackSpeed(3.0) | ||
|
||
mapboxNavigation.setNavigationRoutes(dcShortRoute.toNavigationRoutes()) | ||
mapboxNavigation.startTripSession() | ||
mockLocationReplayerRule.playRoute(dcShortRoute.routeResponse.routes().first()) | ||
mapboxNavigation.routeProgressUpdates().first { | ||
it.currentState == RouteProgressState.COMPLETE | ||
} | ||
mapboxNavigation.stopTripSession() | ||
|
||
verifyResult( | ||
EventNavigationStateChanged(EventNavigationStateChanged.State.NavStarted), | ||
EventDepart(), | ||
EventArrive(), | ||
EventNavigationStateChanged(EventNavigationStateChanged.State.NavEnded), | ||
) | ||
} | ||
|
||
@Test | ||
fun freeDriveWithFeedback() = sdkTest(200_000) { | ||
val dcShorRoute = RoutesProvider.dc_very_short_two_legs(activity) | ||
mapboxNavigation = createMapboxNavigation(activity) | ||
val feedbackType = "feedbackType" | ||
val description = "description" | ||
val screenshot = "screenshot" | ||
val feedbackSubType = arrayOf("subType1", "subType2") | ||
|
||
mapboxNavigation.startTripSession() | ||
mockLocationReplayerRule.playRoute(dcShorRoute.routeResponse.routes().first()) | ||
mapboxNavigation.rawLocationUpdates().passed(20.0).first() | ||
mapboxNavigation.postUserFeedback( | ||
feedbackType = feedbackType, | ||
description = description, | ||
feedbackSource = "na", | ||
screenshot = "screenshot", | ||
feedbackSubType = feedbackSubType | ||
) | ||
mapboxNavigation.rawLocationUpdates().passed(20.0).first() | ||
mapboxNavigation.stopTripSession() | ||
|
||
verifyResult( | ||
EventFreeDrive(EventFreeDrive.Type.Start), | ||
EventFeedback( | ||
driverMode = EventBase.DriverMode.FreeDrive, | ||
feedbackType = feedbackType, | ||
description = description, | ||
feedbackSubType = feedbackSubType, | ||
// TODO fixme when NN implement data_ref | ||
screenshot = "c2NyZWVuc2hvdA==", | ||
), | ||
EventFreeDrive(EventFreeDrive.Type.Stop), | ||
) | ||
} | ||
|
||
private suspend fun verifyResult(vararg expected: EventBase) { | ||
waitForEventsBeSent(expected.size) { eventsAccumulatorRule.events.size } | ||
val result = expected.asList().verifyEvents(eventsAccumulatorRule.events) | ||
|
||
check(result.isEmpty()) { | ||
result.prettyErrors() | ||
} | ||
} | ||
|
||
private fun MockRoute.setRouteAsOriginLocation() { | ||
mockLocationUpdatesRule.pushLocationUpdate( | ||
mockLocationUpdatesRule.generateLocationUpdate { | ||
latitude = this@setRouteAsOriginLocation.routeWaypoints.first().latitude() | ||
longitude = this@setRouteAsOriginLocation.routeWaypoints.first().longitude() | ||
} | ||
) | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
...st/java/com/mapbox/navigation/instrumentation_tests/utils/events/EventsAccumulatorRule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package com.mapbox.navigation.instrumentation_tests.utils.events | ||
|
||
import com.mapbox.bindgen.Value | ||
import com.mapbox.common.EventsServerOptions | ||
import com.mapbox.common.EventsService | ||
import com.mapbox.common.EventsServiceError | ||
import com.mapbox.common.EventsServiceObserver | ||
import com.mapbox.navigation.utils.internal.logE | ||
import com.mapbox.navigator.Navigator | ||
import org.junit.rules.TestWatcher | ||
import org.junit.runner.Description | ||
|
||
class EventsAccumulatorRule( | ||
mapboxToken: String, | ||
) : TestWatcher() { | ||
|
||
private val eventService = EventsService.getOrCreate( | ||
EventsServerOptions( | ||
mapboxToken, Navigator.getUserAgentFragment(), null | ||
) | ||
) | ||
|
||
private val eventsServiceObserver = object : EventsServiceObserver { | ||
override fun didEncounterError(error: EventsServiceError, events: Value) { | ||
logE(LOG_CATEGORY) { | ||
"Occurred error [$error] when send events: $events" | ||
} | ||
} | ||
|
||
override fun didSendEvents(events: Value) { | ||
_events.addAll(events.contents as List<Value>) | ||
} | ||
} | ||
|
||
private val _events = mutableListOf<Value>() | ||
val events: List<Value> get() = _events | ||
|
||
private companion object { | ||
private const val LOG_CATEGORY = "EventsAccumulatorRule" | ||
} | ||
|
||
override fun starting(description: Description?) { | ||
_events.clear() | ||
eventService.registerObserver(eventsServiceObserver) | ||
} | ||
|
||
override fun finished(description: Description?) { | ||
eventService.unregisterObserver(eventsServiceObserver) | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
...Test/java/com/mapbox/navigation/instrumentation_tests/utils/events/domain/EventsDomain.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package com.mapbox.navigation.instrumentation_tests.utils.events.domain | ||
|
||
internal abstract class EventBase( | ||
driverMode: DriverMode, | ||
) { | ||
abstract val event: String | ||
val eventVersion: Double = 2.4 | ||
val driverMode: String = when (driverMode) { | ||
DriverMode.FreeDrive -> "freeDrive" | ||
DriverMode.ActiveGuidance -> "trip" | ||
} | ||
|
||
enum class DriverMode { | ||
FreeDrive, | ||
ActiveGuidance, | ||
} | ||
} | ||
|
||
internal abstract class EventBaseActiveGuidance( | ||
|
||
) : EventBase(DriverMode.ActiveGuidance) { | ||
|
||
} | ||
|
||
internal class EventFeedback( | ||
driverMode: DriverMode, | ||
val feedbackType: String, | ||
val description: String, | ||
val feedbackSubType: Array<String>, | ||
val screenshot: String, | ||
): EventBase(driverMode) { | ||
override val event: String = "navigation.feedback" | ||
} | ||
|
||
internal class EventFreeDrive( | ||
eventType: Type, | ||
) : EventBase(DriverMode.FreeDrive) { | ||
override val event: String = "navigation.freeDrive" | ||
val eventType: String = when (eventType) { | ||
Type.Start -> "start" | ||
Type.Stop -> "stop" | ||
} | ||
|
||
enum class Type { | ||
Start, | ||
Stop, | ||
} | ||
} | ||
|
||
internal class EventNavigationStateChanged( | ||
state: State, | ||
) : EventBase(DriverMode.ActiveGuidance) { | ||
override val event: String = "navigation.navigationStateChanged" | ||
val state: String = when (state) { | ||
State.NavStarted -> "navigation_started" | ||
State.NavEnded -> "navigation_ended" | ||
} | ||
|
||
enum class State { | ||
NavStarted, | ||
NavEnded, | ||
} | ||
} | ||
|
||
internal class EventDepart : EventBaseActiveGuidance() { | ||
override val event: String = "navigation.depart" | ||
} | ||
|
||
internal class EventArrive : EventBaseActiveGuidance() { | ||
override val event: String = "navigation.arrive" | ||
} | ||
|
||
/** | ||
* "volumeLevel" -> {Value@20958} "33" | ||
"driverMode" -> {Value@20960} "freeDrive" | ||
"batteryPluggedIn" -> {Value@20962} "false" | ||
"eventVersion" -> {Value@20964} "2.4" | ||
"simulation" -> {Value@20966} "true" | ||
"audioType" -> {Value@20968} "headphones" | ||
"percentTimeInPortrait" -> {Value@20970} "100" | ||
"operatingSystem" -> {Value@20972} "Android 11" | ||
"driverModeId" -> {Value@20974} "6283cc3d-6262-4833-86f5-663581327e2f" | ||
"connectivity" -> {Value@20976} "Unknown" | ||
"driverModeStartTimestamp" -> {Value@20978} "2023-05-17T16:35:26.530Z" | ||
"percentTimeInForeground" -> {Value@20980} "100" | ||
"driverModeStartTimestampMonotime" -> {Value@20982} "526084002909708" | ||
"event" -> {Value@20984} "navigation.freeDrive" | ||
"lat" -> {Value@20986} "38.89514907122847" | ||
"batteryLevel" -> {Value@20988} "100" | ||
"lng" -> {Value@20990} "-77.03195362937024" | ||
"created" -> {Value@20992} "2023-05-17T16:35:33.859Z" | ||
"eventType" -> {Value@20994} "stop" | ||
"version" -> {Value@20996} "2.4" | ||
"sdkIdentifier" -> {Value@20998} "mapbox-navigation-android" | ||
"createdMonotime" -> {Value@21000} "526091271247708" | ||
"locationEngine" -> {Value@21002} "fused" | ||
"screenBrightness" -> {Value@21004} "40" | ||
"device" -> {Value@21006} "generic_x86 (x86; Android SDK built for x86)" | ||
*/ |
Oops, something went wrong.