Fix MessageRendererTest.kt
Tests were failing because coroutines-test was not used and objects were not properly mocked. Additionally another test has been added Affected: MessageRendererTest.kt
This commit is contained in:
parent
228af2d845
commit
5c3f788eaa
|
@ -27,13 +27,13 @@ import ch.protonmail.android.di.AttachmentsDirectory
|
|||
import ch.protonmail.android.jobs.helper.EmbeddedImage
|
||||
import ch.protonmail.android.utils.extensions.forEachAsync
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.Default
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.channels.actor
|
||||
import kotlinx.coroutines.channels.toList
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.newSingleThreadContext
|
||||
import kotlinx.coroutines.plus
|
||||
import me.proton.core.util.kotlin.DispatcherProvider
|
||||
import org.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Document
|
||||
import org.jsoup.select.Elements
|
||||
|
@ -42,6 +42,7 @@ import java.io.File
|
|||
import javax.inject.Inject
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.time.milliseconds
|
||||
|
||||
/**
|
||||
* A class that will inline the images in the message's body.
|
||||
|
@ -54,11 +55,12 @@ import kotlin.math.sqrt
|
|||
* @author Davide Farella
|
||||
*/
|
||||
internal class MessageRenderer(
|
||||
private val dispatchers: DispatcherProvider,
|
||||
private val directory: File,
|
||||
private val documentParser: DocumentParser,
|
||||
private val bitmapImageDecoder: ImageDecoder,
|
||||
scope: CoroutineScope
|
||||
) : CoroutineScope by ( scope + Default ) {
|
||||
) : CoroutineScope by scope + dispatchers.Comp {
|
||||
|
||||
/** The [String] html of the message body */
|
||||
var messageBody: String? = null
|
||||
|
@ -79,7 +81,7 @@ internal class MessageRenderer(
|
|||
imageCompressor.send(embeddedImages)
|
||||
// Workaround that ignore values for the next half second, since ViewModel is emitting
|
||||
// too many times
|
||||
delay(500)
|
||||
delay(DebounceDelay)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,6 +211,7 @@ internal class MessageRenderer(
|
|||
* @param imageDecoder [ImageDecoder]
|
||||
*/
|
||||
class Factory @Inject constructor(
|
||||
private val dispatchers: DispatcherProvider,
|
||||
@AttachmentsDirectory private val attachmentsDirectory: File,
|
||||
private val documentParser: DocumentParser = DefaultDocumentParser(),
|
||||
private val imageDecoder: ImageDecoder = DefaultImageDecoder()
|
||||
|
@ -218,7 +221,11 @@ internal class MessageRenderer(
|
|||
|
||||
/** @return new instance of [MessageRenderer] with the given [messageBody] */
|
||||
fun create(scope: CoroutineScope, messageId: String) =
|
||||
MessageRenderer(messageDirectory(messageId), documentParser, imageDecoder, scope)
|
||||
MessageRenderer(dispatchers, messageDirectory(messageId), documentParser, imageDecoder, scope)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DebounceDelay = 500.milliseconds
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,50 +16,113 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonMail. If not, see https://www.gnu.org/licenses/.
|
||||
*/
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package ch.protonmail.android.activities.messageDetails
|
||||
|
||||
import android.util.Base64
|
||||
import ch.protonmail.android.jobs.helper.EmbeddedImage
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers.Unconfined
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.channels.consumeEach
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.TestCoroutineScope
|
||||
import kotlinx.coroutines.plus
|
||||
import me.proton.core.test.kotlin.CoroutinesTest
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.TemporaryFolder
|
||||
|
||||
/**
|
||||
* Test class for [MessageRenderer]
|
||||
* @author Davide Farella
|
||||
*/
|
||||
internal class MessageRendererTest {
|
||||
internal class MessageRendererTest : CoroutinesTest {
|
||||
|
||||
private val mockImageDecoder: ImageDecoder = mockk()
|
||||
private val mockDocumentParser: DocumentParser = mockk()
|
||||
@get:Rule
|
||||
val folder: TemporaryFolder = TemporaryFolder()
|
||||
.also { it.create() }
|
||||
|
||||
private val mockImageDecoder: ImageDecoder = mockk(relaxed = true)
|
||||
private val mockDocumentParser: DocumentParser = mockk(relaxed = true)
|
||||
private val mockEmbeddedImages = (1..10).map {
|
||||
mockk<EmbeddedImage>(relaxed = true) { every { localFileName } returns "" }
|
||||
}
|
||||
|
||||
private val CoroutineScope.newRenderer get() =
|
||||
MessageRenderer(mockk(), mockDocumentParser, mockImageDecoder, this)
|
||||
.apply { messageBody = "" }
|
||||
|
||||
@Test // TODO, replace with `runBlockingTest` and `advanceTimeBy` when this is resolved: java.lang.IllegalStateException: This job has not completed yet
|
||||
fun `renderedBody emits just once for every images sent`() {
|
||||
val count = 2
|
||||
val consumer: (String) -> Unit = mockk(relaxed = true)
|
||||
with(TestCoroutineScope().newRenderer) {
|
||||
launch { renderedBody.consumeEach(consumer) }
|
||||
repeat(count) { runBlocking {
|
||||
images.send(mockEmbeddedImages)
|
||||
delay(550)
|
||||
} }
|
||||
renderedBody.close()
|
||||
mockk<EmbeddedImage>(relaxed = true) {
|
||||
every { localFileName } returns "$it"
|
||||
every { contentId } returns "id"
|
||||
every { encoding } returns ""
|
||||
}
|
||||
verify(exactly = count) { consumer(any()) }
|
||||
}
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
for (image in mockEmbeddedImages) {
|
||||
val file = folder.newFile(image.localFileName)
|
||||
file.writeText("_")
|
||||
}
|
||||
}
|
||||
|
||||
private fun CoroutineScope.Renderer() =
|
||||
MessageRenderer(dispatchers, folder.root, mockDocumentParser, mockImageDecoder, this)
|
||||
.apply { messageBody = "" }
|
||||
|
||||
@Test
|
||||
fun `renderedBody doesn't emit for images sent with too short delay`() = coroutinesTest {
|
||||
mockkStatic(Base64::class) {
|
||||
every { Base64.encodeToString(any(), any()) } returns "string"
|
||||
|
||||
val scope = this + Job()
|
||||
val renderer = scope.Renderer()
|
||||
|
||||
val count = 2
|
||||
val consumer: (String) -> Unit = mockk(relaxed = true)
|
||||
|
||||
with(renderer) {
|
||||
launch(Unconfined) {
|
||||
renderedBody.consumeEach(consumer)
|
||||
}
|
||||
repeat(count) {
|
||||
images.offer(mockEmbeddedImages)
|
||||
}
|
||||
advanceUntilIdle()
|
||||
renderedBody.close()
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
verify(exactly = 1) { consumer(any()) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renderedBody emits for every image sent with right delay`() = coroutinesTest {
|
||||
mockkStatic(Base64::class) {
|
||||
every { Base64.encodeToString(any(), any()) } returns "string"
|
||||
|
||||
val scope = this + Job()
|
||||
val renderer = scope.Renderer()
|
||||
|
||||
val count = 2
|
||||
val consumer: (String) -> Unit = mockk(relaxed = true)
|
||||
|
||||
with(renderer) {
|
||||
launch(Unconfined) {
|
||||
renderedBody.consumeEach(consumer)
|
||||
}
|
||||
repeat(count) {
|
||||
images.offer(mockEmbeddedImages)
|
||||
advanceTimeBy(MessageRenderer.DebounceDelay.toLongMilliseconds())
|
||||
}
|
||||
advanceUntilIdle()
|
||||
renderedBody.close()
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
verify(exactly = count) { consumer(any()) }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ import studio.forface.easygradle.dsl.android.*
|
|||
fun initVersions() {
|
||||
|
||||
// region Kotlin
|
||||
`kotlin version` = "1.3.70" // Released: Mar 03, 2020
|
||||
`coroutines version` = "1.3.4" // Released: Mar 06, 2020
|
||||
`kotlin version` = "1.3.72" // Released: Apr 14, 2020
|
||||
`coroutines version` = "1.3.9" // Released: Aug 14, 2020
|
||||
`serialization version` = "0.20.0" // Released: Mar 04, 2020
|
||||
// endregion
|
||||
|
||||
|
@ -69,7 +69,7 @@ const val `Proton-work-manager version` = "0.1.1" // Released: Sep
|
|||
// Test
|
||||
const val `Proton-android-test version` = "0.1" // Released: May 30, 2020
|
||||
const val `Proton-android-instr-test version` = "0.1.2" // Released: Sep 14, 2020
|
||||
const val `Proton-kotlin-test version` = "0.1" // Released: Jun 10, 2020
|
||||
const val `Proton-kotlin-test version` = "0.1.1" // Released: Sep 16, 2020
|
||||
|
||||
const val `Proton-domain version` = "0.1" // Released: Jul 03, 2020
|
||||
|
||||
|
|
Loading…
Reference in New Issue