Skip to content

Commit 50bd79e

Browse files
committed
wip
1 parent 89273ea commit 50bd79e

File tree

14 files changed

+518
-17
lines changed

14 files changed

+518
-17
lines changed

instrumentation-tests/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ dependencies {
4545
implementation project(':libnavigation-android')
4646
implementation project(':libnavui-dropin')
4747
implementation project(":libnavui-util")
48+
implementation project(':libnavigator')
4849

4950
// test
5051
androidTestImplementation project(':libtesting-ui')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
package com.mapbox.navigation.instrumentation_tests.core
2+
3+
import android.content.Context
4+
import android.location.Location
5+
import androidx.test.platform.app.InstrumentationRegistry
6+
import com.mapbox.bindgen.Value
7+
import com.mapbox.common.SettingsServiceFactory
8+
import com.mapbox.common.SettingsServiceStorageType
9+
import com.mapbox.geojson.Point
10+
import com.mapbox.navigation.base.options.DeviceProfile
11+
import com.mapbox.navigation.base.options.NavigationOptions
12+
import com.mapbox.navigation.base.trip.model.RouteProgressState
13+
import com.mapbox.navigation.core.MapboxNavigation
14+
import com.mapbox.navigation.core.MapboxNavigationProvider
15+
import com.mapbox.navigation.core.telemetry.events.FeedbackEvent
16+
import com.mapbox.navigation.instrumentation_tests.activity.EmptyTestActivity
17+
import com.mapbox.navigation.instrumentation_tests.utils.events.EventsAccumulatorRule
18+
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventArrive
19+
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventBase
20+
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventDepart
21+
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventFeedback
22+
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventFreeDrive
23+
import com.mapbox.navigation.instrumentation_tests.utils.events.domain.EventNavigationStateChanged
24+
import com.mapbox.navigation.instrumentation_tests.utils.events.verify.verifyEvents
25+
import com.mapbox.navigation.instrumentation_tests.utils.http.EventsRequestHandle
26+
import com.mapbox.navigation.instrumentation_tests.utils.location.MockLocationReplayerRule
27+
import com.mapbox.navigation.instrumentation_tests.utils.routes.MockRoute
28+
import com.mapbox.navigation.instrumentation_tests.utils.routes.RoutesProvider
29+
import com.mapbox.navigation.instrumentation_tests.utils.routes.RoutesProvider.toNavigationRoutes
30+
import com.mapbox.navigation.testing.ui.BaseTest
31+
import com.mapbox.navigation.testing.ui.utils.MapboxNavigationRule
32+
import com.mapbox.navigation.testing.ui.utils.coroutines.passed
33+
import com.mapbox.navigation.testing.ui.utils.coroutines.rawLocationUpdates
34+
import com.mapbox.navigation.testing.ui.utils.coroutines.routeProgressUpdates
35+
import com.mapbox.navigation.testing.ui.utils.coroutines.sdkTest
36+
import com.mapbox.navigation.testing.ui.utils.getMapboxAccessTokenFromResources
37+
import com.mapbox.navigation.testing.ui.utils.runOnMainSync
38+
import kotlinx.coroutines.delay
39+
import kotlinx.coroutines.flow.first
40+
import org.junit.Before
41+
import org.junit.Rule
42+
import org.junit.Test
43+
44+
class TelemetryEventsTest : BaseTest<EmptyTestActivity>(EmptyTestActivity::class.java) {
45+
46+
@get:Rule
47+
val eventsAccumulatorRule = EventsAccumulatorRule(
48+
getMapboxAccessTokenFromResources(InstrumentationRegistry.getInstrumentation().targetContext)
49+
)
50+
51+
@get:Rule
52+
val mockLocationReplayerRule = MockLocationReplayerRule(mockLocationUpdatesRule)
53+
54+
@get:Rule
55+
val mapboxNavigationRule = MapboxNavigationRule()
56+
57+
private val coordinates = listOf(
58+
Point.fromLngLat(-77.031991, 38.894721),
59+
Point.fromLngLat(-77.031991, 38.895433),
60+
Point.fromLngLat(-77.030923, 38.895433),
61+
)
62+
63+
private lateinit var mapboxNavigation: MapboxNavigation
64+
private val eventsRequestHandle = EventsRequestHandle()
65+
66+
private companion object {
67+
private const val LOG_CATEGORY = "TelemetryEventsTest"
68+
69+
/* See mapbox_common_settings_internal.hpp in common repo */
70+
private const val TELEMETRY_EVENTS_BASE_URL_PROPERTY =
71+
"com.mapbox.common.telemetry.internal.events_base_url_override"
72+
73+
private fun createMapboxNavigation(context: Context): MapboxNavigation =
74+
MapboxNavigationProvider.create(
75+
NavigationOptions.Builder(context)
76+
.accessToken(getMapboxAccessTokenFromResources(context))
77+
.deviceProfile(
78+
DeviceProfile.Builder()
79+
.customConfig(
80+
"""
81+
{
82+
"features": {
83+
"useTelemetryNavigationEvents": true
84+
},
85+
"telemetry": {
86+
"eventsPriority": "Immediate"
87+
}
88+
}
89+
""".trimIndent()
90+
)
91+
.build()
92+
)
93+
.build()
94+
)
95+
96+
/**
97+
* Give the chance to send events: 5 attempts with 1 seconds delay
98+
*/
99+
private suspend fun waitForEventsBeSent(expectedEvents: Int, current: () -> Int) {
100+
IntRange(0, 4).forEach { _ ->
101+
if (expectedEvents == current()) {
102+
return
103+
} else {
104+
delay(1_000)
105+
}
106+
}
107+
}
108+
109+
private fun List<String>.prettyErrors(): String = joinToString(postfix = "\n")
110+
}
111+
112+
override fun setupMockLocation(): Location = mockLocationUpdatesRule.generateLocationUpdate {
113+
latitude = coordinates[0].latitude()
114+
longitude = coordinates[0].longitude()
115+
}
116+
117+
@Before
118+
fun setup() {
119+
runOnMainSync {
120+
// substitute telemetry base url
121+
SettingsServiceFactory.getInstance(SettingsServiceStorageType.NON_PERSISTENT).apply {
122+
set(TELEMETRY_EVENTS_BASE_URL_PROPERTY, Value(mockWebServerRule.baseUrl))
123+
}
124+
125+
mockWebServerRule.requestHandlers.add(eventsRequestHandle)
126+
}
127+
}
128+
129+
@Test
130+
fun freeDrivePlain() = sdkTest {
131+
val dcShorRoute = RoutesProvider.dc_short_with_alternative(activity).apply {
132+
setRouteAsOriginLocation()
133+
}
134+
mapboxNavigation = createMapboxNavigation(activity)
135+
136+
mapboxNavigation.startTripSession()
137+
mockLocationReplayerRule.playRoute(dcShorRoute.routeResponse.routes().first())
138+
mapboxNavigation.rawLocationUpdates().passed(50.0).first()
139+
mapboxNavigation.stopTripSession()
140+
141+
verifyResult(
142+
EventFreeDrive(EventFreeDrive.Type.Start),
143+
EventFreeDrive(EventFreeDrive.Type.Stop),
144+
)
145+
}
146+
147+
@Test
148+
fun activeGuidancePlain() = sdkTest {
149+
val dcShortRoute = RoutesProvider.dc_short_with_alternative(activity).apply {
150+
setRouteAsOriginLocation()
151+
}
152+
mapboxNavigation = createMapboxNavigation(activity)
153+
mockLocationReplayerRule.playbackSpeed(3.0)
154+
155+
mapboxNavigation.setNavigationRoutes(dcShortRoute.toNavigationRoutes())
156+
mapboxNavigation.startTripSession()
157+
mockLocationReplayerRule.playRoute(dcShortRoute.routeResponse.routes().first())
158+
mapboxNavigation.routeProgressUpdates().first {
159+
it.currentState == RouteProgressState.COMPLETE
160+
}
161+
mapboxNavigation.stopTripSession()
162+
163+
verifyResult(
164+
EventNavigationStateChanged(EventNavigationStateChanged.State.NavStarted),
165+
EventDepart(),
166+
EventArrive(),
167+
EventNavigationStateChanged(EventNavigationStateChanged.State.NavEnded),
168+
)
169+
}
170+
171+
@Test
172+
fun freeDriveWithFeedback() = sdkTest(200_000) {
173+
val dcShorRoute = RoutesProvider.dc_very_short_two_legs(activity)
174+
mapboxNavigation = createMapboxNavigation(activity)
175+
val feedbackType = "feedbackType"
176+
val description = "description"
177+
val screenshot = "screenshot"
178+
val feedbackSubType = arrayOf("subType1", "subType2")
179+
180+
mapboxNavigation.startTripSession()
181+
mockLocationReplayerRule.playRoute(dcShorRoute.routeResponse.routes().first())
182+
mapboxNavigation.rawLocationUpdates().passed(20.0).first()
183+
mapboxNavigation.postUserFeedback(
184+
feedbackType = feedbackType,
185+
description = description,
186+
feedbackSource = "na",
187+
screenshot = "screenshot",
188+
feedbackSubType = feedbackSubType
189+
)
190+
mapboxNavigation.rawLocationUpdates().passed(20.0).first()
191+
mapboxNavigation.stopTripSession()
192+
193+
verifyResult(
194+
EventFreeDrive(EventFreeDrive.Type.Start),
195+
EventFeedback(
196+
driverMode = EventBase.DriverMode.FreeDrive,
197+
feedbackType = feedbackType,
198+
description = description,
199+
feedbackSubType = feedbackSubType,
200+
// TODO fixme when NN implement data_ref
201+
screenshot = "c2NyZWVuc2hvdA==",
202+
),
203+
EventFreeDrive(EventFreeDrive.Type.Stop),
204+
)
205+
}
206+
207+
private suspend fun verifyResult(vararg expected: EventBase) {
208+
waitForEventsBeSent(expected.size) { eventsAccumulatorRule.events.size }
209+
val result = expected.asList().verifyEvents(eventsAccumulatorRule.events)
210+
211+
check(result.isEmpty()) {
212+
result.prettyErrors()
213+
}
214+
}
215+
216+
private fun MockRoute.setRouteAsOriginLocation() {
217+
mockLocationUpdatesRule.pushLocationUpdate(
218+
mockLocationUpdatesRule.generateLocationUpdate {
219+
latitude = this@setRouteAsOriginLocation.routeWaypoints.first().latitude()
220+
longitude = this@setRouteAsOriginLocation.routeWaypoints.first().longitude()
221+
}
222+
)
223+
}
224+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.mapbox.navigation.instrumentation_tests.utils.events
2+
3+
import com.mapbox.bindgen.Value
4+
import com.mapbox.common.EventsServerOptions
5+
import com.mapbox.common.EventsService
6+
import com.mapbox.common.EventsServiceError
7+
import com.mapbox.common.EventsServiceObserver
8+
import com.mapbox.navigation.utils.internal.logE
9+
import com.mapbox.navigator.Navigator
10+
import org.junit.rules.TestWatcher
11+
import org.junit.runner.Description
12+
13+
class EventsAccumulatorRule(
14+
mapboxToken: String,
15+
) : TestWatcher() {
16+
17+
private val eventService = EventsService.getOrCreate(
18+
EventsServerOptions(
19+
mapboxToken, Navigator.getUserAgentFragment(), null
20+
)
21+
)
22+
23+
private val eventsServiceObserver = object : EventsServiceObserver {
24+
override fun didEncounterError(error: EventsServiceError, events: Value) {
25+
logE(LOG_CATEGORY) {
26+
"Occurred error [$error] when send events: $events"
27+
}
28+
}
29+
30+
override fun didSendEvents(events: Value) {
31+
_events.addAll(events.contents as List<Value>)
32+
}
33+
}
34+
35+
private val _events = mutableListOf<Value>()
36+
val events: List<Value> get() = _events
37+
38+
private companion object {
39+
private const val LOG_CATEGORY = "EventsAccumulatorRule"
40+
}
41+
42+
override fun starting(description: Description?) {
43+
_events.clear()
44+
eventService.registerObserver(eventsServiceObserver)
45+
}
46+
47+
override fun finished(description: Description?) {
48+
eventService.unregisterObserver(eventsServiceObserver)
49+
}
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package com.mapbox.navigation.instrumentation_tests.utils.events.domain
2+
3+
internal abstract class EventBase(
4+
driverMode: DriverMode,
5+
) {
6+
abstract val event: String
7+
val eventVersion: Double = 2.4
8+
val driverMode: String = when (driverMode) {
9+
DriverMode.FreeDrive -> "freeDrive"
10+
DriverMode.ActiveGuidance -> "trip"
11+
}
12+
13+
enum class DriverMode {
14+
FreeDrive,
15+
ActiveGuidance,
16+
}
17+
}
18+
19+
internal abstract class EventBaseActiveGuidance(
20+
21+
) : EventBase(DriverMode.ActiveGuidance) {
22+
23+
}
24+
25+
internal class EventFeedback(
26+
driverMode: DriverMode,
27+
val feedbackType: String,
28+
val description: String,
29+
val feedbackSubType: Array<String>,
30+
val screenshot: String,
31+
): EventBase(driverMode) {
32+
override val event: String = "navigation.feedback"
33+
}
34+
35+
internal class EventFreeDrive(
36+
eventType: Type,
37+
) : EventBase(DriverMode.FreeDrive) {
38+
override val event: String = "navigation.freeDrive"
39+
val eventType: String = when (eventType) {
40+
Type.Start -> "start"
41+
Type.Stop -> "stop"
42+
}
43+
44+
enum class Type {
45+
Start,
46+
Stop,
47+
}
48+
}
49+
50+
internal class EventNavigationStateChanged(
51+
state: State,
52+
) : EventBase(DriverMode.ActiveGuidance) {
53+
override val event: String = "navigation.navigationStateChanged"
54+
val state: String = when (state) {
55+
State.NavStarted -> "navigation_started"
56+
State.NavEnded -> "navigation_ended"
57+
}
58+
59+
enum class State {
60+
NavStarted,
61+
NavEnded,
62+
}
63+
}
64+
65+
internal class EventDepart : EventBaseActiveGuidance() {
66+
override val event: String = "navigation.depart"
67+
}
68+
69+
internal class EventArrive : EventBaseActiveGuidance() {
70+
override val event: String = "navigation.arrive"
71+
}
72+
73+
/**
74+
* "volumeLevel" -> {Value@20958} "33"
75+
"driverMode" -> {Value@20960} "freeDrive"
76+
"batteryPluggedIn" -> {Value@20962} "false"
77+
"eventVersion" -> {Value@20964} "2.4"
78+
"simulation" -> {Value@20966} "true"
79+
"audioType" -> {Value@20968} "headphones"
80+
"percentTimeInPortrait" -> {Value@20970} "100"
81+
"operatingSystem" -> {Value@20972} "Android 11"
82+
"driverModeId" -> {Value@20974} "6283cc3d-6262-4833-86f5-663581327e2f"
83+
"connectivity" -> {Value@20976} "Unknown"
84+
"driverModeStartTimestamp" -> {Value@20978} "2023-05-17T16:35:26.530Z"
85+
"percentTimeInForeground" -> {Value@20980} "100"
86+
"driverModeStartTimestampMonotime" -> {Value@20982} "526084002909708"
87+
"event" -> {Value@20984} "navigation.freeDrive"
88+
"lat" -> {Value@20986} "38.89514907122847"
89+
"batteryLevel" -> {Value@20988} "100"
90+
"lng" -> {Value@20990} "-77.03195362937024"
91+
"created" -> {Value@20992} "2023-05-17T16:35:33.859Z"
92+
"eventType" -> {Value@20994} "stop"
93+
"version" -> {Value@20996} "2.4"
94+
"sdkIdentifier" -> {Value@20998} "mapbox-navigation-android"
95+
"createdMonotime" -> {Value@21000} "526091271247708"
96+
"locationEngine" -> {Value@21002} "fused"
97+
"screenBrightness" -> {Value@21004} "40"
98+
"device" -> {Value@21006} "generic_x86 (x86; Android SDK built for x86)"
99+
*/

0 commit comments

Comments
 (0)