Skip to content

Commit 1424d80

Browse files
committed
invoke HistoryRecordingStateChangeObserver#onShouldStartRecording with current state
1 parent e60a15a commit 1424d80

File tree

6 files changed

+338
-212
lines changed

6 files changed

+338
-212
lines changed

instrumentation-tests/src/androidTest/java/com/mapbox/navigation/instrumentation_tests/core/HistoryRecordingStateChangeObserverTest.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,41 @@ class HistoryRecordingStateChangeObserverTest :
196196
checkHasNoNextElement(eventsChannel)
197197
}
198198

199+
@Test
200+
fun history_recording_observer_receives_current_state_event_for_active_sessions() = sdkTest {
201+
createMapboxNavigation()
202+
val nonEmptyRoutes = RoutesProvider.dc_very_short(activity).toNavigationRoutes()
203+
val eventsChannelIdle = Channel<HistoryRecordingStateChangeEvent>(Channel.UNLIMITED)
204+
val eventsChannelFreeDrive = Channel<HistoryRecordingStateChangeEvent>(Channel.UNLIMITED)
205+
val eventsChannelActiveGuidance =
206+
Channel<HistoryRecordingStateChangeEvent>(Channel.UNLIMITED)
207+
208+
observeHistoryRecordingEvents(eventsChannelIdle)
209+
checkHasNoNextElement(eventsChannelIdle)
210+
211+
mapboxNavigation.startTripSession()
212+
observeHistoryRecordingEvents(eventsChannelFreeDrive)
213+
214+
assertEquals(
215+
HistoryRecordingStateChangeEvent(
216+
HistoryRecordingStateChangeEventType.START,
217+
HistoryRecordingSessionState.FreeDrive::class
218+
),
219+
eventsChannelFreeDrive.receive()
220+
)
221+
222+
mapboxNavigation.setNavigationRoutesAndWaitForUpdate(nonEmptyRoutes)
223+
observeHistoryRecordingEvents(eventsChannelActiveGuidance)
224+
225+
assertEquals(
226+
HistoryRecordingStateChangeEvent(
227+
HistoryRecordingStateChangeEventType.START,
228+
HistoryRecordingSessionState.ActiveGuidance::class
229+
),
230+
eventsChannelActiveGuidance.receive()
231+
)
232+
}
233+
199234
@Test
200235
fun history_recording_observer_route_refresh() = sdkTest {
201236
val mockRoute = RoutesProvider.dc_very_short(activity)

libnavigation-core/src/main/java/com/mapbox/navigation/core/HistoryRecordingStateHandler.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ internal class HistoryRecordingStateHandler : TripSessionStateObserver {
2525

2626
fun registerStateChangeObserver(observer: HistoryRecordingStateChangeObserver) {
2727
historyRecordingStateChangeObservers.add(observer)
28+
if (shouldNotifyOnStart(currentState)) {
29+
observer.onShouldStartRecording(currentState)
30+
}
2831
}
2932

3033
fun unregisterStateChangeObserver(observer: HistoryRecordingStateChangeObserver) {
@@ -113,10 +116,14 @@ internal class HistoryRecordingStateHandler : TripSessionStateObserver {
113116
finishRecordingBlock(it, oldState)
114117
}
115118
}
116-
if (newState !is HistoryRecordingSessionState.Idle) {
119+
if (shouldNotifyOnStart(newState)) {
117120
historyRecordingStateChangeObservers.forEach { it.onShouldStartRecording(newState) }
118121
}
119122
copilotSessionObservers.forEach { it.onCopilotSessionChanged(newState) }
120123
}
121124
}
125+
126+
private fun shouldNotifyOnStart(state: HistoryRecordingSessionState): Boolean {
127+
return state !is HistoryRecordingSessionState.Idle
128+
}
122129
}

libnavigation-core/src/main/java/com/mapbox/navigation/core/internal/extensions/MapboxNavigationExtensions.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ import com.mapbox.navigation.core.internal.HistoryRecordingStateChangeObserver
1010
* Register [HistoryRecordingStateChangeObserver]. Use this method to receive notifications
1111
* regarding history recording: when to start, stop or cancel recording
1212
* to have each trip session (Free Drive and Active Guidance) recorded independently.
13-
* NOTE: call this method before [MapboxNavigation.startTripSession] and
14-
* [MapboxNavigation.setNavigationRoutes] invocations.
13+
* NOTE: if there is a session running when the observer is being registered,
14+
* it will be notified via [HistoryRecordingStateChangeObserver.onShouldStartRecording]
15+
* with the current session as an argument.
1516
*
1617
* @param observer callback to receive notifications.
1718
*/
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
package com.mapbox.navigation.core.internal
2+
3+
import com.mapbox.navigation.core.CopilotSessionObserver
4+
import com.mapbox.navigation.core.HistoryRecordingStateHandler
5+
import com.mapbox.navigation.core.trip.session.TripSessionState
6+
import io.mockk.clearMocks
7+
import io.mockk.mockk
8+
import io.mockk.verify
9+
import kotlinx.coroutines.ExperimentalCoroutinesApi
10+
import org.junit.Assert.assertTrue
11+
import org.junit.Before
12+
import org.junit.Test
13+
14+
@OptIn(ExperimentalCoroutinesApi::class)
15+
class HistoryRecordingStateHandlerCopilotTest {
16+
17+
private val copilotObserver = mockk<CopilotSessionObserver>(relaxUnitFun = true)
18+
private lateinit var stateHandler: HistoryRecordingStateHandler
19+
20+
@Before
21+
fun setUp() {
22+
stateHandler = HistoryRecordingStateHandler()
23+
}
24+
25+
@Test
26+
fun currentCopilotSessionShouldBeIdle() {
27+
assertTrue(stateHandler.currentCopilotSession() is HistoryRecordingSessionState.Idle)
28+
}
29+
30+
@Test
31+
fun currentCopilotSessionShouldBeFreeDrive() {
32+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
33+
assertTrue(stateHandler.currentCopilotSession() is HistoryRecordingSessionState.FreeDrive)
34+
}
35+
36+
@Test
37+
fun currentCopilotSessionShouldBeActiveGuidance() {
38+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
39+
stateHandler.setRoutes(listOf(mockk()))
40+
assertTrue(
41+
stateHandler.currentCopilotSession() is HistoryRecordingSessionState.ActiveGuidance
42+
)
43+
}
44+
45+
@Test
46+
fun shouldNotifyCopilotObserverOnRegister() {
47+
stateHandler.registerCopilotSessionObserver(copilotObserver)
48+
49+
verify {
50+
copilotObserver.onCopilotSessionChanged(ofType<HistoryRecordingSessionState.Idle>())
51+
}
52+
}
53+
54+
@Test
55+
fun shouldNotifyCopilotObserverOnChangeFromIdleToFreeDrive() {
56+
stateHandler.registerCopilotSessionObserver(copilotObserver)
57+
58+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
59+
60+
verify {
61+
copilotObserver.onCopilotSessionChanged(
62+
ofType<HistoryRecordingSessionState.FreeDrive>()
63+
)
64+
}
65+
}
66+
67+
@Test
68+
fun shouldNotifyCopilotObserverOnChangeFromIdleToActiveGuidance() {
69+
stateHandler.registerCopilotSessionObserver(copilotObserver)
70+
stateHandler.setRoutes(listOf(mockk()))
71+
clearMocks(copilotObserver, answers = false)
72+
73+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
74+
75+
verify {
76+
copilotObserver.onCopilotSessionChanged(
77+
ofType<HistoryRecordingSessionState.ActiveGuidance>()
78+
)
79+
}
80+
}
81+
82+
@Test
83+
fun shouldNotNotifyCopilotObserverOnChangeFromIdleToIdle() {
84+
stateHandler.registerCopilotSessionObserver(copilotObserver)
85+
clearMocks(copilotObserver, answers = false)
86+
87+
stateHandler.onSessionStateChanged(TripSessionState.STOPPED)
88+
89+
verify(exactly = 0) { copilotObserver.onCopilotSessionChanged(any()) }
90+
}
91+
92+
@Test
93+
fun shouldNotifyCopilotObserverOnChangeFromFreeDriveToIdle() {
94+
stateHandler.registerCopilotSessionObserver(copilotObserver)
95+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
96+
clearMocks(copilotObserver, answers = false)
97+
98+
stateHandler.onSessionStateChanged(TripSessionState.STOPPED)
99+
100+
verify {
101+
copilotObserver.onCopilotSessionChanged(ofType<HistoryRecordingSessionState.Idle>())
102+
}
103+
}
104+
105+
@Test
106+
fun shouldNotNotifyCopilotObserverOnChangeFromFreeDriveToFreeDrive() {
107+
stateHandler.registerCopilotSessionObserver(copilotObserver)
108+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
109+
clearMocks(copilotObserver, answers = false)
110+
111+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
112+
113+
verify(exactly = 0) { copilotObserver.onCopilotSessionChanged(any()) }
114+
}
115+
116+
@Test
117+
fun shouldNotifyCopilotObserverOnChangeFromFreeDriveToActiveGuidance() {
118+
stateHandler.registerCopilotSessionObserver(copilotObserver)
119+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
120+
clearMocks(copilotObserver, answers = false)
121+
122+
stateHandler.setRoutes(listOf(mockk()))
123+
124+
verify {
125+
copilotObserver.onCopilotSessionChanged(
126+
ofType<HistoryRecordingSessionState.ActiveGuidance>()
127+
)
128+
}
129+
}
130+
131+
@Test
132+
fun shouldNotifyCopilotObserverOnChangeFromActiveGuidanceToIdle() {
133+
stateHandler.registerCopilotSessionObserver(copilotObserver)
134+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
135+
stateHandler.setRoutes(listOf(mockk()))
136+
clearMocks(copilotObserver, answers = false)
137+
138+
stateHandler.onSessionStateChanged(TripSessionState.STOPPED)
139+
140+
verify {
141+
copilotObserver.onCopilotSessionChanged(ofType<HistoryRecordingSessionState.Idle>())
142+
}
143+
}
144+
145+
@Test
146+
fun shouldNotifyCopilotObserverOnChangeFromActiveGuidanceToFreeDrive() {
147+
stateHandler.registerCopilotSessionObserver(copilotObserver)
148+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
149+
stateHandler.setRoutes(listOf(mockk()))
150+
clearMocks(copilotObserver, answers = false)
151+
152+
stateHandler.setRoutes(emptyList())
153+
154+
verify {
155+
copilotObserver.onCopilotSessionChanged(
156+
ofType<HistoryRecordingSessionState.FreeDrive>()
157+
)
158+
}
159+
}
160+
161+
@Test
162+
fun shouldNotNotifyCopilotObserverOnChangeFromActiveGuidanceToActiveGuidance() {
163+
stateHandler.registerCopilotSessionObserver(copilotObserver)
164+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
165+
stateHandler.setRoutes(listOf(mockk()))
166+
clearMocks(copilotObserver, answers = false)
167+
168+
stateHandler.setRoutes(listOf(mockk(), mockk()))
169+
170+
verify(exactly = 0) { copilotObserver.onCopilotSessionChanged(any()) }
171+
}
172+
173+
@Test
174+
fun copilotObserversNotification() {
175+
val observer1 = mockk<CopilotSessionObserver>(relaxUnitFun = true)
176+
val observer2 = mockk<CopilotSessionObserver>(relaxUnitFun = true)
177+
val observer3 = mockk<CopilotSessionObserver>(relaxUnitFun = true)
178+
stateHandler = HistoryRecordingStateHandler()
179+
180+
// no observers
181+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
182+
183+
verifyNoInteractions(observer1, observer2, observer3)
184+
185+
// 1 observer
186+
stateHandler.registerCopilotSessionObserver(observer1)
187+
verify(exactly = 1) {
188+
observer1.onCopilotSessionChanged(ofType<HistoryRecordingSessionState.FreeDrive>())
189+
}
190+
stateHandler.setRoutes(listOf(mockk()))
191+
192+
verify(exactly = 1) {
193+
observer1.onCopilotSessionChanged(ofType<HistoryRecordingSessionState.ActiveGuidance>())
194+
}
195+
verifyNoInteractions(observer2, observer3)
196+
197+
// 3 observers
198+
clearMocks(observer1, observer2, observer3)
199+
200+
stateHandler.registerCopilotSessionObserver(observer2)
201+
stateHandler.registerCopilotSessionObserver(observer3)
202+
stateHandler.onSessionStateChanged(TripSessionState.STOPPED)
203+
204+
verify(exactly = 1) {
205+
observer1.onCopilotSessionChanged(ofType<HistoryRecordingSessionState.Idle>())
206+
observer2.onCopilotSessionChanged(ofType<HistoryRecordingSessionState.Idle>())
207+
observer3.onCopilotSessionChanged(ofType<HistoryRecordingSessionState.Idle>())
208+
}
209+
210+
// 2 observers
211+
clearMocks(observer1, observer2, observer3)
212+
213+
stateHandler.unregisterCopilotSessionObserver(observer2)
214+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
215+
stateHandler.setRoutes(listOf(mockk()))
216+
217+
verify(exactly = 1) {
218+
observer1.onCopilotSessionChanged(ofType<HistoryRecordingSessionState.ActiveGuidance>())
219+
observer3.onCopilotSessionChanged(ofType<HistoryRecordingSessionState.ActiveGuidance>())
220+
}
221+
verifyNoInteractions(observer2)
222+
223+
// no observers
224+
clearMocks(observer1, observer2, observer3)
225+
226+
stateHandler.unregisterAllCopilotSessionObservers()
227+
stateHandler.setRoutes(emptyList())
228+
229+
verifyNoInteractions(observer1, observer2, observer3)
230+
}
231+
232+
private fun verifyNoInteractions(vararg observers: CopilotSessionObserver) {
233+
observers.forEach {
234+
verify(exactly = 0) {
235+
it.onCopilotSessionChanged(any())
236+
}
237+
}
238+
}
239+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.mapbox.navigation.core.internal
2+
3+
import com.mapbox.navigation.core.HistoryRecordingStateHandler
4+
import com.mapbox.navigation.core.trip.session.TripSessionState
5+
import io.mockk.mockk
6+
import io.mockk.verify
7+
import kotlinx.coroutines.ExperimentalCoroutinesApi
8+
import org.junit.Test
9+
10+
@OptIn(ExperimentalCoroutinesApi::class)
11+
class HistoryRecordingStateHandlerObserverRegisterTest {
12+
13+
private val observer = mockk<HistoryRecordingStateChangeObserver>(relaxUnitFun = true)
14+
private val stateHandler = HistoryRecordingStateHandler()
15+
16+
@Test
17+
fun shouldNotNotifyWithIdleState() {
18+
stateHandler.registerStateChangeObserver(observer)
19+
verify(exactly = 0) {
20+
observer.onShouldStartRecording(any())
21+
observer.onShouldStopRecording(any())
22+
observer.onShouldCancelRecording(any())
23+
}
24+
}
25+
26+
@Test
27+
fun shouldNotifyWithFreeDriveState() {
28+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
29+
stateHandler.registerStateChangeObserver(observer)
30+
verify {
31+
observer.onShouldStartRecording(ofType<HistoryRecordingSessionState.FreeDrive>())
32+
}
33+
verify(exactly = 0) {
34+
observer.onShouldStopRecording(any())
35+
observer.onShouldCancelRecording(any())
36+
}
37+
}
38+
39+
@Test
40+
fun shouldNotifyWithActiveGuidanceState() {
41+
stateHandler.onSessionStateChanged(TripSessionState.STARTED)
42+
stateHandler.setRoutes(listOf(mockk()))
43+
stateHandler.registerStateChangeObserver(observer)
44+
verify {
45+
observer.onShouldStartRecording(ofType<HistoryRecordingSessionState.ActiveGuidance>())
46+
}
47+
verify(exactly = 0) {
48+
observer.onShouldStopRecording(any())
49+
observer.onShouldCancelRecording(any())
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)