mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-26 07:37:08 +00:00
refactor: Use view binding to replace Kotlin synthetics (#824)
* refactor: Migrate home screen to data binding * Add view binding * Migrate ConversationView to view binding * Migrate ConversationActivityV2 to view binding * View model refactor * Move more functionality to the view model * Add ui state events flow * Update conversation item bindings * Update profile picture view bindings * Replace Kotlin synthetics with view bindings * Fix qr code fragment binding and optimize imports * View binding refactors * Make TextSecurePreferences an interface and add an implementation to improve testability * Add conversation repository * Migrate remaining TextSecurePreferences functions into the interface * Add unit conversation unit tests * Add unit test coverage for remaining view model functions
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
package org.thoughtcrime.securesms
|
||||
|
||||
import kotlinx.coroutines.test.TestCoroutineScope
|
||||
import kotlinx.coroutines.test.runBlockingTest
|
||||
import org.junit.Rule
|
||||
|
||||
open class BaseCoroutineTest {
|
||||
|
||||
@get:Rule
|
||||
var coroutinesTestRule = CoroutineTestRule()
|
||||
|
||||
protected fun runBlockingTest(test: suspend TestCoroutineScope.() -> Unit) =
|
||||
coroutinesTestRule.runBlockingTest { test() }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.thoughtcrime.securesms
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||
import kotlinx.coroutines.test.TestCoroutineScope
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.rules.TestWatcher
|
||||
import org.junit.runner.Description
|
||||
|
||||
/**
|
||||
* Sets the main coroutines dispatcher to a [TestCoroutineScope] for unit testing. A
|
||||
* [TestCoroutineScope] provides control over the execution of coroutines.
|
||||
*
|
||||
* Declare it as a JUnit Rule:
|
||||
*
|
||||
* ```
|
||||
* @get:Rule
|
||||
* var coroutineTestRule = CoroutineTestRule()
|
||||
* ```
|
||||
*
|
||||
* Use it directly as a [TestCoroutineScope]:
|
||||
*
|
||||
* ```
|
||||
* coroutineTestRule.pauseDispatcher()
|
||||
* ...
|
||||
* coroutineTestRule.resumeDispatcher()
|
||||
* ...
|
||||
* coroutineTestRule.runBlockingTest { }
|
||||
* ...
|
||||
*
|
||||
*/
|
||||
|
||||
class CoroutineTestRule(
|
||||
val dispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
|
||||
) : TestWatcher(), TestCoroutineScope by TestCoroutineScope(dispatcher) {
|
||||
|
||||
override fun starting(description: Description?) {
|
||||
super.starting(description)
|
||||
Dispatchers.setMain(dispatcher)
|
||||
|
||||
}
|
||||
|
||||
override fun finished(description: Description?) {
|
||||
super.finished(description)
|
||||
cleanupTestCoroutines()
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.thoughtcrime.securesms
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.TimeoutException
|
||||
|
||||
/**
|
||||
* Gets the value of a [LiveData] or waits for it to have one, with a timeout.
|
||||
*
|
||||
* Use this extension from host-side (JVM) tests. It's recommended to use it alongside
|
||||
* `InstantTaskExecutorRule` or a similar mechanism to execute tasks synchronously.
|
||||
*/
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
fun <T> LiveData<T>.getOrAwaitValue(
|
||||
time: Long = 2,
|
||||
timeUnit: TimeUnit = TimeUnit.SECONDS,
|
||||
afterObserve: () -> Unit = {}
|
||||
): T {
|
||||
var data: T? = null
|
||||
val latch = CountDownLatch(1)
|
||||
val observer = object : Observer<T> {
|
||||
override fun onChanged(o: T?) {
|
||||
data = o
|
||||
latch.countDown()
|
||||
this@getOrAwaitValue.removeObserver(this)
|
||||
}
|
||||
}
|
||||
this.observeForever(observer)
|
||||
|
||||
try {
|
||||
afterObserve.invoke()
|
||||
|
||||
// Don't wait indefinitely if the LiveData is not set.
|
||||
if (!latch.await(time, timeUnit)) {
|
||||
throw TimeoutException("LiveData value was never set.")
|
||||
}
|
||||
|
||||
} finally {
|
||||
this.removeObserver(observer)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return data as T
|
||||
}
|
||||
|
||||
/**
|
||||
* Observes a [LiveData] until the `block` is done executing.
|
||||
*/
|
||||
fun <T> LiveData<T>.observeForTesting(block: () -> Unit) {
|
||||
val observer = Observer<T> { }
|
||||
try {
|
||||
observeForever(observer)
|
||||
block()
|
||||
} finally {
|
||||
removeObserver(observer)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user