@@ -20,17 +20,17 @@ import android.content.Context
20
20
import android.net.Uri
21
21
import androidx.lifecycle.Observer
22
22
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
23
- import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
23
+ import kotlinx.coroutines.CoroutineDispatcher
24
+ import kotlinx.coroutines.CoroutineScope
24
25
import kotlinx.coroutines.Dispatchers
25
- import kotlinx.coroutines.GlobalScope
26
+ import kotlinx.coroutines.SupervisorJob
26
27
import kotlinx.coroutines.delay
27
28
import kotlinx.coroutines.launch
28
29
import kotlinx.coroutines.runBlocking
29
30
import kotlinx.coroutines.withTimeout
30
31
import org.junit.Assert.assertEquals
31
32
import org.junit.Assert.assertNotNull
32
33
import org.junit.Assert.assertTrue
33
- import org.matrix.android.sdk.api.Matrix
34
34
import org.matrix.android.sdk.api.MatrixCallback
35
35
import org.matrix.android.sdk.api.MatrixConfiguration
36
36
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
@@ -45,7 +45,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline
45
45
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
46
46
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
47
47
import org.matrix.android.sdk.api.session.sync.SyncState
48
- import java.util.ArrayList
48
+ import timber.log.Timber
49
49
import java.util.UUID
50
50
import java.util.concurrent.CountDownLatch
51
51
import java.util.concurrent.TimeUnit
@@ -56,21 +56,22 @@ import java.util.concurrent.TimeUnit
56
56
*/
57
57
class CommonTestHelper (context : Context ) {
58
58
59
- val matrix: Matrix
59
+ internal val matrix: TestMatrix
60
+ val coroutineScope = CoroutineScope (SupervisorJob () + Dispatchers .Main )
60
61
61
- fun getTestInterceptor (session : Session ): MockOkHttpInterceptor ? = TestNetworkModule .interceptorForSession(session.sessionId) as ? MockOkHttpInterceptor
62
+ fun getTestInterceptor (session : Session ): MockOkHttpInterceptor ? = TestModule .interceptorForSession(session.sessionId) as ? MockOkHttpInterceptor
62
63
63
64
init {
64
65
UiThreadStatement .runOnUiThread {
65
- Matrix .initialize(
66
+ TestMatrix .initialize(
66
67
context,
67
68
MatrixConfiguration (
68
69
applicationFlavor = " TestFlavor" ,
69
70
roomDisplayNameFallbackProvider = TestRoomDisplayNameFallbackProvider ()
70
71
)
71
72
)
72
73
}
73
- matrix = Matrix .getInstance(context)
74
+ matrix = TestMatrix .getInstance(context)
74
75
}
75
76
76
77
fun createAccount (userNamePrefix : String , testParams : SessionTestParams ): Session {
@@ -95,31 +96,45 @@ class CommonTestHelper(context: Context) {
95
96
*
96
97
* @param session the session to sync
97
98
*/
98
- @Suppress(" EXPERIMENTAL_API_USAGE" )
99
- fun syncSession (session : Session , timeout : Long = TestConstants .timeOutMillis) {
99
+ fun syncSession (session : Session , timeout : Long = TestConstants .timeOutMillis * 10) {
100
100
val lock = CountDownLatch (1 )
101
-
102
- val job = GlobalScope .launch(Dispatchers .Main ) {
103
- session.open()
101
+ coroutineScope.launch {
102
+ session.startSync(true )
103
+ val syncLiveData = session.getSyncStateLive()
104
+ val syncObserver = object : Observer <SyncState > {
105
+ override fun onChanged (t : SyncState ? ) {
106
+ if (session.hasAlreadySynced()) {
107
+ lock.countDown()
108
+ syncLiveData.removeObserver(this )
109
+ }
110
+ }
111
+ }
112
+ syncLiveData.observeForever(syncObserver)
104
113
}
105
- runBlocking { job.join() }
106
-
107
- session.startSync(true )
114
+ await(lock, timeout)
115
+ }
108
116
109
- val syncLiveData = runBlocking(Dispatchers .Main ) {
110
- session.getSyncStateLive()
111
- }
112
- val syncObserver = object : Observer <SyncState > {
113
- override fun onChanged (t : SyncState ? ) {
114
- if (session.hasAlreadySynced()) {
115
- lock.countDown()
116
- syncLiveData.removeObserver(this )
117
+ /* *
118
+ * This methods clear the cache and waits for initialSync
119
+ *
120
+ * @param session the session to sync
121
+ */
122
+ fun clearCacheAndSync (session : Session , timeout : Long = TestConstants .timeOutMillis) {
123
+ waitWithLatch(timeout) { latch ->
124
+ session.clearCache()
125
+ val syncLiveData = session.getSyncStateLive()
126
+ val syncObserver = object : Observer <SyncState > {
127
+ override fun onChanged (t : SyncState ? ) {
128
+ if (session.hasAlreadySynced()) {
129
+ Timber .v(" Clear cache and synced" )
130
+ syncLiveData.removeObserver(this )
131
+ latch.countDown()
132
+ }
117
133
}
118
134
}
135
+ syncLiveData.observeForever(syncObserver)
136
+ session.startSync(true )
119
137
}
120
- GlobalScope .launch(Dispatchers .Main ) { syncLiveData.observeForever(syncObserver) }
121
-
122
- await(lock, timeout)
123
138
}
124
139
125
140
/* *
@@ -130,46 +145,57 @@ class CommonTestHelper(context: Context) {
130
145
* @param nbOfMessages the number of time the message will be sent
131
146
*/
132
147
fun sendTextMessage (room : Room , message : String , nbOfMessages : Int , timeout : Long = TestConstants .timeOutMillis): List <TimelineEvent > {
133
- val timeline = room.createTimeline(null , TimelineSettings (10 ))
134
148
val sentEvents = ArrayList <TimelineEvent >(nbOfMessages)
135
- val latch = CountDownLatch (1 )
136
- val timelineListener = object : Timeline .Listener {
137
- override fun onTimelineFailure (throwable : Throwable ) {
138
- }
149
+ val timeline = room.createTimeline(null , TimelineSettings (10 ))
150
+ timeline.start()
151
+ waitWithLatch(timeout + 1_000L * nbOfMessages) { latch ->
152
+ val timelineListener = object : Timeline .Listener {
153
+ override fun onTimelineFailure (throwable : Throwable ) {
154
+ }
139
155
140
- override fun onNewTimelineEvents (eventIds : List <String >) {
141
- // noop
142
- }
156
+ override fun onNewTimelineEvents (eventIds : List <String >) {
157
+ // noop
158
+ }
143
159
144
- override fun onTimelineUpdated (snapshot : List <TimelineEvent >) {
145
- val newMessages = snapshot
146
- .filter { it.root.sendState == SendState .SYNCED }
147
- .filter { it.root.getClearType() == EventType .MESSAGE }
148
- .filter { it.root.getClearContent().toModel<MessageContent >()?.body?.startsWith(message) == true }
149
-
150
- if (newMessages.size == nbOfMessages) {
151
- sentEvents.addAll(newMessages)
152
- // Remove listener now, if not at the next update sendEvents could change
153
- timeline.removeListener(this )
154
- latch.countDown()
160
+ override fun onTimelineUpdated (snapshot : List <TimelineEvent >) {
161
+ val newMessages = snapshot
162
+ .filter { it.root.sendState == SendState .SYNCED }
163
+ .filter { it.root.getClearType() == EventType .MESSAGE }
164
+ .filter { it.root.getClearContent().toModel<MessageContent >()?.body?.startsWith(message) == true }
165
+
166
+ Timber .v(" New synced message size: ${newMessages.size} " )
167
+ if (newMessages.size == nbOfMessages) {
168
+ sentEvents.addAll(newMessages)
169
+ // Remove listener now, if not at the next update sendEvents could change
170
+ timeline.removeListener(this )
171
+ latch.countDown()
172
+ }
155
173
}
156
174
}
175
+ timeline.addListener(timelineListener)
176
+ sendTextMessagesBatched(room, message, nbOfMessages)
157
177
}
158
- timeline.start()
159
- timeline.addListener(timelineListener)
160
- for (i in 0 until nbOfMessages) {
161
- room.sendTextMessage(message + " #" + (i + 1 ))
162
- }
163
- // Wait 3 second more per message
164
- await(latch, timeout = timeout + 3_000L * nbOfMessages)
165
178
timeline.dispose()
166
-
167
179
// Check that all events has been created
168
180
assertEquals(" Message number do not match $sentEvents " , nbOfMessages.toLong(), sentEvents.size.toLong())
169
-
170
181
return sentEvents
171
182
}
172
183
184
+ /* *
185
+ * Will send nb of messages provided by count parameter but waits a bit every 10 messages to avoid gap in sync
186
+ */
187
+ private fun sendTextMessagesBatched (room : Room , message : String , count : Int ) {
188
+ (1 until count + 1 )
189
+ .map { " $message #$it " }
190
+ .chunked(10 )
191
+ .forEach { batchedMessages ->
192
+ batchedMessages.forEach { formattedMessage ->
193
+ room.sendTextMessage(formattedMessage)
194
+ }
195
+ Thread .sleep(1_000L )
196
+ }
197
+ }
198
+
173
199
// PRIVATE METHODS *****************************************************************************
174
200
175
201
/* *
@@ -239,10 +265,10 @@ class CommonTestHelper(context: Context) {
239
265
240
266
assertTrue(registrationResult is RegistrationResult .Success )
241
267
val session = (registrationResult as RegistrationResult .Success ).session
268
+ session.open()
242
269
if (sessionTestParams.withInitialSync) {
243
270
syncSession(session, 60_000 )
244
271
}
245
-
246
272
return session
247
273
}
248
274
@@ -267,7 +293,7 @@ class CommonTestHelper(context: Context) {
267
293
.getLoginWizard()
268
294
.login(userName, password, " myDevice" )
269
295
}
270
-
296
+ session.open()
271
297
if (sessionTestParams.withInitialSync) {
272
298
syncSession(session)
273
299
}
@@ -332,22 +358,21 @@ class CommonTestHelper(context: Context) {
332
358
assertTrue(latch.await(timeout ? : TestConstants .timeOutMillis, TimeUnit .MILLISECONDS ))
333
359
}
334
360
335
- @Suppress(" EXPERIMENTAL_API_USAGE" )
336
- fun retryPeriodicallyWithLatch (latch : CountDownLatch , condition : (() -> Boolean )) {
337
- GlobalScope .launch {
338
- while (true ) {
339
- delay(1000 )
340
- if (condition()) {
341
- latch.countDown()
342
- return @launch
343
- }
361
+ suspend fun retryPeriodicallyWithLatch (latch : CountDownLatch , condition : (() -> Boolean )) {
362
+ while (true ) {
363
+ delay(1000 )
364
+ if (condition()) {
365
+ latch.countDown()
366
+ return
344
367
}
345
368
}
346
369
}
347
370
348
- fun waitWithLatch (timeout : Long? = TestConstants .timeOutMillis, block : (CountDownLatch ) -> Unit ) {
371
+ fun waitWithLatch (timeout : Long? = TestConstants .timeOutMillis, dispatcher : CoroutineDispatcher = Dispatchers . Main , block : suspend (CountDownLatch ) -> Unit ) {
349
372
val latch = CountDownLatch (1 )
350
- block(latch)
373
+ coroutineScope.launch(dispatcher) {
374
+ block(latch)
375
+ }
351
376
await(latch, timeout)
352
377
}
353
378
0 commit comments