1
+ package io.eigr.synapsys.extensions.android.sensors
2
+
3
+ import android.content.Context
4
+ import android.hardware.Sensor
5
+ import android.hardware.Sensor.TYPE_ACCELEROMETER
6
+ import android.hardware.SensorEvent
7
+ import android.hardware.SensorManager
8
+ import androidx.test.core.app.ApplicationProvider
9
+ import io.eigr.synapsys.core.actor.Actor
10
+ import io.eigr.synapsys.core.actor.ActorSystem
11
+ import io.eigr.synapsys.core.actor.Config
12
+ import io.eigr.synapsys.core.actor.RestartStrategy
13
+ import io.eigr.synapsys.core.actor.Supervisor
14
+ import io.eigr.synapsys.core.actor.SupervisorStrategy
15
+ import io.eigr.synapsys.core.internals.BaseActorAdapter
16
+ import io.eigr.synapsys.core.internals.mailbox.Mailbox
17
+ import io.eigr.synapsys.core.internals.mailbox.MailboxAbstractQueue
18
+ import io.eigr.synapsys.core.internals.mailbox.transport.ChannelMailbox
19
+ import io.eigr.synapsys.core.internals.scheduler.ActorExecutor
20
+ import io.eigr.synapsys.extensions.android.sensors.actor.SensorActor
21
+ import io.eigr.synapsys.extensions.android.sensors.events.SensorData
22
+ import junit.framework.TestCase.assertFalse
23
+ import junit.framework.TestCase.assertTrue
24
+ import kotlinx.coroutines.delay
25
+ import kotlinx.coroutines.runBlocking
26
+ import kotlinx.coroutines.test.runTest
27
+ import org.junit.After
28
+ import org.junit.Before
29
+ import org.junit.Test
30
+ import org.junit.runner.RunWith
31
+ import org.robolectric.RobolectricTestRunner
32
+ import org.robolectric.Shadows
33
+ import org.robolectric.shadows.ShadowSensor
34
+ import org.robolectric.shadows.ShadowSensorManager
35
+ import org.slf4j.LoggerFactory
36
+ import io.eigr.synapsys.core.actor.Context as ActorContext
37
+ import org.robolectric.annotation.Config as RoboConfig
38
+
39
+ @RunWith(RobolectricTestRunner ::class )
40
+ @RoboConfig(manifest = RoboConfig .NONE )
41
+ class AndroidSchedulerIntegrationTest {
42
+ private lateinit var system: ActorSystem
43
+ private val config = Config (maxReductions = 100 )
44
+ private val androidContext = ApplicationProvider .getApplicationContext<Context >()
45
+ private val scheduler = AndroidScheduler (config)
46
+ private lateinit var shadowSensorManager: ShadowSensorManager
47
+
48
+ @Before
49
+ fun setup () {
50
+ system = ActorSystem .create(config, scheduler)
51
+ shadowSensorManager = Shadows .shadowOf(
52
+ androidContext.getSystemService(Context .SENSOR_SERVICE ) as SensorManager
53
+ )
54
+ }
55
+
56
+ @After
57
+ fun tearDown () {
58
+ scheduler.cleanAllWorkerQueues()
59
+ }
60
+
61
+ @Test
62
+ fun `enqueue should add SensorActor to sensorActors map` () {
63
+ val sensorActor = TestSensorActor (
64
+ id = " sensor-1" ,
65
+ initialState = State (0 ),
66
+ context = androidContext
67
+ )
68
+
69
+ scheduler.enqueue(createExecutor(sensorActor))
70
+
71
+ sensorActor.id?.let { scheduler.removeActor(it) }?.let { assertTrue(it) }
72
+ }
73
+
74
+ @Test
75
+ fun `enqueue should delegate non-sensor actors to working stealing scheduler` () {
76
+ val regularActor = RegularActor (" regular-1" , 0 )
77
+ val executor = createExecutor(regularActor)
78
+
79
+ scheduler.enqueue(executor)
80
+
81
+ regularActor.id?.let { scheduler.removeActor(it) }?.let { assertTrue(it) }
82
+ }
83
+
84
+ @Test
85
+ fun `cleanAllWorkerQueues should remove all actors` () {
86
+ val sensorExecutor = createExecutor(
87
+ TestSensorActor (id = " sensor-1" , initialState = State (0 ), context = androidContext)
88
+ )
89
+
90
+ val regularExecutor = createExecutor(RegularActor (id = " regular-1" , initialState = 0 ))
91
+
92
+ scheduler.enqueue(sensorExecutor)
93
+ scheduler.enqueue(regularExecutor)
94
+ scheduler.cleanAllWorkerQueues()
95
+
96
+ assertFalse(scheduler.removeActor(" sensor-1" ))
97
+ assertFalse(scheduler.removeActor(" regular-1" ))
98
+ }
99
+
100
+ @Test
101
+ fun `should deliver sensor events to actor` () = runTest {
102
+
103
+ val testSensor = ShadowSensor .newInstance(TYPE_ACCELEROMETER )
104
+ shadowSensorManager.addSensor(testSensor)
105
+
106
+ val testActor = TestSensorActor (
107
+ id = " sensor-1" ,
108
+ initialState = State (0 ),
109
+ context = androidContext
110
+ )
111
+
112
+ // system.actorOf(id = "sensor-1", initialState = Unit) { id, state -> TestSensorActor(id, state, androidContext)}
113
+ scheduler.enqueue(createExecutor(testActor))
114
+
115
+ for (i in 1 .. 1000 ) {
116
+ shadowSensorManager.sendSensorEventToListeners(
117
+ createSensorEvent(
118
+ testSensor,
119
+ floatArrayOf(1.0f , 2.0f , 3.0f )
120
+ )
121
+ )
122
+ }
123
+
124
+ runBlocking {
125
+ delay(5000 )
126
+ }
127
+ }
128
+
129
+ private fun createSensorEvent (sensor : Sensor , values : FloatArray ): SensorEvent {
130
+ return ShadowSensorManager .createSensorEvent(values.size, TYPE_ACCELEROMETER )
131
+ }
132
+
133
+ private fun <S : Any , M : Any , R : Any > createExecutor (actor : Actor <S , M , R >): ActorExecutor <* > {
134
+ actor.system = system
135
+
136
+ val adapter = BaseActorAdapter (actor, actor.system)
137
+ val mailbox = Mailbox (queue = ChannelMailbox <M >() as MailboxAbstractQueue <M >)
138
+ val supervisor = Supervisor (
139
+ id = " root-supervisor" ,
140
+ strategy = SupervisorStrategy (RestartStrategy .OneForOne , 5 )
141
+ )
142
+
143
+ return ActorExecutor (adapter, mailbox, supervisor.getMessageChannel())
144
+ }
145
+
146
+ data class State (var count : Int = 0 )
147
+
148
+ class TestSensorActor (
149
+ id : String ,
150
+ initialState : State ,
151
+ context : Context ,
152
+ sensorType : Int = TYPE_ACCELEROMETER ,
153
+ samplingPeriod : Int = SensorManager .SENSOR_DELAY_NORMAL
154
+ ) : SensorActor<State, SensorData>(
155
+ id = id,
156
+ initialState = initialState,
157
+ androidContext = context,
158
+ sensorType = sensorType,
159
+ samplingPeriod = samplingPeriod
160
+ ) {
161
+ private val log = LoggerFactory .getLogger(TestSensorActor ::class .java)
162
+
163
+ override fun onReceive (
164
+ message : SensorData ,
165
+ ctx : ActorContext <State >
166
+ ): Pair <ActorContext <State >, Unit> {
167
+ log.info(" Message data: {}" , message)
168
+ val state: Int = ctx.state?.count?.plus(1 ) ? : 0
169
+ log.info(" New State: {}" , state)
170
+ return ctx.withState(newState = State (count = state)) to Unit
171
+ }
172
+ }
173
+
174
+ class RegularActor <S : Any >(
175
+ id : String ,
176
+ initialState : S ?
177
+ ) : Actor<S, String, String>(id, initialState) {
178
+ override fun onReceive (
179
+ message : String ,
180
+ ctx : ActorContext <S >
181
+ ): Pair <ActorContext <S >, String> {
182
+ println (" Received message: $message " )
183
+ return ctx to " processed: $message "
184
+ }
185
+ }
186
+ }
0 commit comments