Android tests: introduce TestBackgroundDetectionObserver so sync is not cancelled while testing + fix small warnings

This commit is contained in:
ganfra 2021-11-19 17:18:39 +01:00
parent 67975e0c83
commit 69720ffdd3
12 changed files with 97 additions and 79 deletions

View file

@ -35,16 +35,16 @@ import org.matrix.android.sdk.internal.SessionManager
import org.matrix.android.sdk.internal.network.ApiInterceptor
import org.matrix.android.sdk.internal.network.UserAgentHolder
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
import org.matrix.olm.OlmManager
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
/**
* This is the main entry point to the matrix sdk.
* To get the singleton instance, use getInstance static method.
* This mimics the Matrix class but using TestMatrixComponent internally instead of regular MatrixComponent.
*/
class Matrix private constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
class TestMatrix constructor(context: Context, matrixConfiguration: MatrixConfiguration) {
@Inject internal lateinit var legacySessionImporter: LegacySessionImporter
@Inject internal lateinit var authenticationService: AuthenticationService
@ -55,15 +55,18 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
@Inject internal lateinit var sessionManager: SessionManager
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
@Inject internal lateinit var apiInterceptor: ApiInterceptor
@Inject internal lateinit var matrixWorkerFactory: MatrixWorkerFactory
private val uiHandler = Handler(Looper.getMainLooper())
init {
Monarchy.init(context)
DaggerTestMatrixComponent.factory().create(context, matrixConfiguration).inject(this)
if (context.applicationContext !is Configuration.Provider) {
WorkManager.initialize(context, Configuration.Builder().setExecutor(Executors.newCachedThreadPool()).build())
}
val configuration = Configuration.Builder()
.setExecutor(Executors.newCachedThreadPool())
.setWorkerFactory(matrixWorkerFactory)
.build()
WorkManager.initialize(context, configuration)
uiHandler.post {
ProcessLifecycleOwner.get().lifecycle.addObserver(backgroundDetectionObserver)
}
@ -93,21 +96,21 @@ class Matrix private constructor(context: Context, matrixConfiguration: MatrixCo
companion object {
private lateinit var instance: Matrix
private lateinit var instance: TestMatrix
private val isInit = AtomicBoolean(false)
fun initialize(context: Context, matrixConfiguration: MatrixConfiguration) {
if (isInit.compareAndSet(false, true)) {
instance = Matrix(context.applicationContext, matrixConfiguration)
instance = TestMatrix(context.applicationContext, matrixConfiguration)
}
}
fun getInstance(context: Context): Matrix {
fun getInstance(context: Context): TestMatrix {
if (isInit.compareAndSet(false, true)) {
val appContext = context.applicationContext
if (appContext is MatrixConfiguration.Provider) {
val matrixConfiguration = (appContext as MatrixConfiguration.Provider).providesMatrixConfiguration()
instance = Matrix(appContext, matrixConfiguration)
instance = TestMatrix(appContext, matrixConfiguration)
} else {
throw IllegalStateException("Matrix is not initialized properly." +
" You should call Matrix.initialize or let your application implements MatrixConfiguration.Provider.")

View file

@ -20,7 +20,6 @@ import android.content.Context
import android.net.Uri
import androidx.lifecycle.Observer
import androidx.test.internal.runner.junit4.statement.UiThreadStatement
import androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
@ -30,9 +29,9 @@ import kotlinx.coroutines.withTimeout
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.TestMatrix
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
import org.matrix.android.sdk.api.session.Session
@ -45,7 +44,6 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings
import org.matrix.android.sdk.api.session.sync.SyncState
import java.util.ArrayList
import java.util.UUID
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
@ -56,13 +54,13 @@ import java.util.concurrent.TimeUnit
*/
class CommonTestHelper(context: Context) {
val matrix: Matrix
val matrix: TestMatrix
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestNetworkModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
fun getTestInterceptor(session: Session): MockOkHttpInterceptor? = TestModule.interceptorForSession(session.sessionId) as? MockOkHttpInterceptor
init {
UiThreadStatement.runOnUiThread {
Matrix.initialize(
TestMatrix.initialize(
context,
MatrixConfiguration(
applicationFlavor = "TestFlavor",
@ -70,7 +68,7 @@ class CommonTestHelper(context: Context) {
)
)
}
matrix = Matrix.getInstance(context)
matrix = TestMatrix.getInstance(context)
}
fun createAccount(userNamePrefix: String, testParams: SessionTestParams): Session {

View file

@ -20,6 +20,7 @@ import android.content.Context
import dagger.BindsInstance
import dagger.Component
import org.matrix.android.sdk.api.MatrixConfiguration
import org.matrix.android.sdk.api.TestMatrix
import org.matrix.android.sdk.internal.auth.AuthModule
import org.matrix.android.sdk.internal.di.MatrixComponent
import org.matrix.android.sdk.internal.di.MatrixModule
@ -34,12 +35,13 @@ import org.matrix.android.sdk.internal.util.system.SystemModule
NetworkModule::class,
AuthModule::class,
RawModule::class,
SystemModule::class,
TestNetworkModule::class
SystemModule::class
])
@MatrixScope
internal interface TestMatrixComponent : MatrixComponent {
fun inject(matrix: TestMatrix)
@Component.Factory
interface Factory {
fun create(@BindsInstance context: Context,

View file

@ -18,10 +18,43 @@ package org.matrix.android.sdk.common
import dagger.Binds
import dagger.Module
import dagger.Provides
import org.matrix.android.sdk.internal.di.MatrixComponent
import org.matrix.android.sdk.internal.di.MatrixScope
import org.matrix.android.sdk.internal.session.MockHttpInterceptor
import org.matrix.android.sdk.internal.session.TestInterceptor
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
import org.matrix.android.sdk.internal.util.DefaultBackgroundDetectionObserver
import org.matrix.android.sdk.internal.util.TestBackgroundDetectionObserver
@Module
internal abstract class TestModule {
@Binds
abstract fun providesMatrixComponent(testMatrixComponent: TestMatrixComponent): MatrixComponent
@Module
companion object {
val interceptors = ArrayList<TestInterceptor>()
fun interceptorForSession(sessionId: String): TestInterceptor? = interceptors.firstOrNull { it.sessionId == sessionId }
@Provides
@JvmStatic
@MockHttpInterceptor
fun providesTestInterceptor(): TestInterceptor? {
return MockOkHttpInterceptor().also {
interceptors.add(it)
}
}
@Provides
@JvmStatic
@MatrixScope
fun providesBackgroundDetectionObserver(): BackgroundDetectionObserver {
return TestBackgroundDetectionObserver()
}
}
}

View file

@ -1,39 +0,0 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.matrix.android.sdk.common
import dagger.Module
import dagger.Provides
import org.matrix.android.sdk.internal.session.MockHttpInterceptor
import org.matrix.android.sdk.internal.session.TestInterceptor
@Module
internal object TestNetworkModule {
val interceptors = ArrayList<TestInterceptor>()
fun interceptorForSession(sessionId: String): TestInterceptor? = interceptors.firstOrNull { it.sessionId == sessionId }
@Provides
@JvmStatic
@MockHttpInterceptor
fun providesTestInterceptor(): TestInterceptor? {
return MockOkHttpInterceptor().also {
interceptors.add(it)
}
}
}

View file

@ -45,7 +45,7 @@ object RoomDataHelper {
content: Content? = null,
prevContent: Content? = null,
sender: String = FAKE_TEST_SENDER,
stateKey: String = FAKE_TEST_SENDER
stateKey: String? =null
): Event {
return Event(
type = type,
@ -64,6 +64,6 @@ object RoomDataHelper {
private fun createFakeRoomMemberEvent(): Event {
val roomMember = RoomMemberContent(Membership.JOIN, "Fake name #${Random.nextLong()}").toContent()
return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember)
return createFakeEvent(EventType.STATE_ROOM_MEMBER, roomMember, stateKey = FAKE_TEST_SENDER)
}
}

View file

@ -540,6 +540,7 @@ class SpaceHierarchyTest : InstrumentedTest {
}
@Test
@Suppress("EXPERIMENTAL_API_USAGE")
fun testParentRelation() {
val aliceSession = commonTestHelper.createAccount("Alice", SessionTestParams(true))
val bobSession = commonTestHelper.createAccount("Bib", SessionTestParams(true))

View file

@ -22,22 +22,31 @@ import org.matrix.android.sdk.internal.di.MatrixScope
import timber.log.Timber
import javax.inject.Inject
/**
* To be attached to ProcessLifecycleOwner lifecycle
*/
@MatrixScope
internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecycleObserver {
var isInBackground: Boolean = true
interface BackgroundDetectionObserver: DefaultLifecycleObserver {
val isInBackground: Boolean
fun register(listener: Listener)
fun unregister(listener: Listener)
interface Listener {
fun onMoveToForeground()
fun onMoveToBackground()
}
}
internal class DefaultBackgroundDetectionObserver: BackgroundDetectionObserver {
override var isInBackground: Boolean = true
private set
private val listeners = LinkedHashSet<Listener>()
private val listeners = LinkedHashSet<BackgroundDetectionObserver.Listener>()
fun register(listener: Listener) {
override fun register(listener: BackgroundDetectionObserver.Listener) {
listeners.add(listener)
}
fun unregister(listener: Listener) {
override fun unregister(listener: BackgroundDetectionObserver.Listener) {
listeners.remove(listener)
}
@ -52,9 +61,17 @@ internal class BackgroundDetectionObserver @Inject constructor() : DefaultLifecy
isInBackground = true
listeners.forEach { it.onMoveToBackground() }
}
interface Listener {
fun onMoveToForeground()
fun onMoveToBackground()
}
}
/**
* Force foreground for testing
*/
internal class TestBackgroundDetectionObserver : BackgroundDetectionObserver {
override val isInBackground: Boolean = false
override fun register(listener: BackgroundDetectionObserver.Listener) = Unit
override fun unregister(listener: BackgroundDetectionObserver.Listener) = Unit
}

View file

@ -48,6 +48,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.TestMatrix
import org.matrix.android.sdk.api.session.Session
@RunWith(AndroidJUnit4::class)
@ -62,7 +63,7 @@ class SecurityBootstrapTest : VerificationTestBase() {
@Before
fun createSessionWithCrossSigning() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val matrix = Matrix.getInstance(context)
val matrix = TestMatrix.getInstance(context)
val userName = "foobar_${System.currentTimeMillis()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
stubAllExternalIntents()

View file

@ -25,8 +25,8 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
import org.junit.Assert
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.TestMatrix
import org.matrix.android.sdk.api.auth.data.HomeServerConnectionConfig
import org.matrix.android.sdk.api.auth.registration.RegistrationResult
import org.matrix.android.sdk.api.session.Session
@ -41,7 +41,7 @@ abstract class VerificationTestBase {
protected val uiTestBase = OnboardingRobot()
fun createAccountAndSync(matrix: Matrix,
fun createAccountAndSync(matrix: TestMatrix,
userName: String,
password: String,
withInitialSync: Boolean): Session {

View file

@ -42,6 +42,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.TestMatrix
import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.UserPasswordAuth
@ -67,7 +68,7 @@ class VerifySessionInteractiveTest : VerificationTestBase() {
@Before
fun createSessionWithCrossSigning() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val matrix = Matrix.getInstance(context)
val matrix = TestMatrix.getInstance(context)
val userName = "foobar_${System.currentTimeMillis()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
doSync<Unit> {

View file

@ -46,6 +46,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.matrix.android.sdk.api.Matrix
import org.matrix.android.sdk.api.TestMatrix
import org.matrix.android.sdk.api.auth.UIABaseAuth
import org.matrix.android.sdk.api.auth.UserInteractiveAuthInterceptor
import org.matrix.android.sdk.api.auth.UserPasswordAuth
@ -67,7 +68,7 @@ class VerifySessionPassphraseTest : VerificationTestBase() {
@Before
fun createSessionWithCrossSigningAnd4S() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val matrix = Matrix.getInstance(context)
val matrix = TestMatrix.getInstance(context)
val userName = "foobar_${System.currentTimeMillis()}"
existingSession = createAccountAndSync(matrix, userName, password, true)
doSync<Unit> {