Browse Source

Add tests for AndroidMediaPreProcessor

pull/1925/head
Benoit Marty 10 months ago committed by Benoit Marty
parent
commit
7e8794bc6e
  1. 10
      libraries/mediaupload/impl/build.gradle.kts
  2. 4
      libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt
  3. BIN
      libraries/mediaupload/impl/src/test/assets/animated_gif.gif
  4. BIN
      libraries/mediaupload/impl/src/test/assets/image.png
  5. BIN
      libraries/mediaupload/impl/src/test/assets/sample3s.mp3
  6. BIN
      libraries/mediaupload/impl/src/test/assets/text.txt
  7. BIN
      libraries/mediaupload/impl/src/test/assets/video.mp4
  8. 331
      libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessorTest.kt

10
libraries/mediaupload/impl/build.gradle.kts

@ -27,6 +27,12 @@ android { @@ -27,6 +27,12 @@ android {
generateDaggerFactories.set(true)
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
dependencies {
implementation(projects.anvilannotations)
anvil(projects.anvilcodegen)
@ -37,6 +43,7 @@ android { @@ -37,6 +43,7 @@ android {
implementation(projects.libraries.core)
implementation(projects.libraries.di)
implementation(projects.libraries.matrix.api)
implementation(projects.services.toolbox.api)
implementation(libs.inject)
implementation(libs.androidx.exifinterface)
implementation(libs.coroutines.core)
@ -44,7 +51,10 @@ android { @@ -44,7 +51,10 @@ android {
implementation(libs.vanniktech.blurhash)
testImplementation(libs.test.junit)
testImplementation(libs.test.robolectric)
testImplementation(libs.coroutines.test)
testImplementation(libs.test.truth)
testImplementation(projects.tests.testutils)
testImplementation(projects.services.toolbox.test)
}
}

4
libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/ThumbnailFactory.kt

@ -32,6 +32,7 @@ import io.element.android.libraries.androidutils.media.runAndRelease @@ -32,6 +32,7 @@ import io.element.android.libraries.androidutils.media.runAndRelease
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.matrix.api.media.ThumbnailInfo
import io.element.android.services.toolbox.api.sdk.BuildVersionSdkIntProvider
import kotlinx.coroutines.suspendCancellableCoroutine
import java.io.File
import javax.inject.Inject
@ -56,13 +57,14 @@ private const val VIDEO_THUMB_FRAME = 0L @@ -56,13 +57,14 @@ private const val VIDEO_THUMB_FRAME = 0L
class ThumbnailFactory @Inject constructor(
@ApplicationContext private val context: Context,
private val sdkIntProvider: BuildVersionSdkIntProvider
) {
@SuppressLint("NewApi")
suspend fun createImageThumbnail(file: File): ThumbnailResult {
return createThumbnail { cancellationSignal ->
// This API works correctly with GIF
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (sdkIntProvider.isAtLeast(Build.VERSION_CODES.Q)) {
ThumbnailUtils.createImageThumbnail(
file,
Size(THUMB_MAX_WIDTH, THUMB_MAX_HEIGHT),

BIN
libraries/mediaupload/impl/src/test/assets/animated_gif.gif (Stored with Git LFS)

Binary file not shown.

BIN
libraries/mediaupload/impl/src/test/assets/image.png (Stored with Git LFS)

Binary file not shown.

BIN
libraries/mediaupload/impl/src/test/assets/sample3s.mp3 (Stored with Git LFS)

Binary file not shown.

BIN
libraries/mediaupload/impl/src/test/assets/text.txt (Stored with Git LFS)

Binary file not shown.

BIN
libraries/mediaupload/impl/src/test/assets/video.mp4 (Stored with Git LFS)

Binary file not shown.

331
libraries/mediaupload/impl/src/test/kotlin/io/element/android/libraries/mediaupload/AndroidMediaPreProcessorTest.kt

@ -0,0 +1,331 @@ @@ -0,0 +1,331 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* 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 io.element.android.libraries.mediaupload
import android.content.Context
import android.os.Build
import androidx.core.net.toUri
import androidx.test.platform.app.InstrumentationRegistry
import com.google.common.truth.Truth.assertThat
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.matrix.api.media.AudioInfo
import io.element.android.libraries.matrix.api.media.FileInfo
import io.element.android.libraries.matrix.api.media.ImageInfo
import io.element.android.libraries.matrix.api.media.ThumbnailInfo
import io.element.android.libraries.matrix.api.media.VideoInfo
import io.element.android.libraries.mediaupload.api.MediaPreProcessor
import io.element.android.libraries.mediaupload.api.MediaUploadInfo
import io.element.android.services.toolbox.test.sdk.FakeBuildVersionSdkIntProvider
import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import java.io.File
import java.io.FileNotFoundException
import java.io.IOException
import kotlin.time.Duration
@RunWith(RobolectricTestRunner::class)
class AndroidMediaPreProcessorTest {
@Test
fun `test processing image`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val sut = createAndroidMediaPreProcessor(context)
val file = getFileFromAssets(context, "image.png")
val result = sut.process(
uri = file.toUri(),
mimeType = MimeTypes.Png,
deleteOriginal = false,
compressIfPossible = true,
).getOrThrow()
assertThat(result.file.path).endsWith("image.png")
val info = (result as MediaUploadInfo.Image)
assertThat(info.thumbnailFile).isNull() // TODO Check this
assertThat(info.imageInfo).isEqualTo(
ImageInfo(
height = 1_178,
width = 1_818,
mimetype = MimeTypes.Png,
size = 114_867,
thumbnailInfo = null,
thumbnailSource = null,
blurhash = null,
)
)
assertThat(file.exists()).isTrue()
}
@Test
fun `test processing image api Q`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val sut = createAndroidMediaPreProcessor(context, sdkIntVersion = Build.VERSION_CODES.Q)
val file = getFileFromAssets(context, "image.png")
val result = sut.process(
uri = file.toUri(),
mimeType = MimeTypes.Png,
deleteOriginal = false,
compressIfPossible = true,
).getOrThrow()
assertThat(result.file.path).endsWith("image.png")
val info = (result as MediaUploadInfo.Image)
assertThat(info.thumbnailFile).isNull() // TODO Check this
assertThat(info.imageInfo).isEqualTo(
ImageInfo(
height = 1_178,
width = 1_818,
mimetype = MimeTypes.Png,
size = 114_867,
thumbnailInfo = null,
thumbnailSource = null,
blurhash = null,
)
)
assertThat(file.exists()).isTrue()
}
@Test
fun `test processing image no compression`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val sut = createAndroidMediaPreProcessor(context)
val file = getFileFromAssets(context, "image.png")
val result = sut.process(
uri = file.toUri(),
mimeType = MimeTypes.Png,
deleteOriginal = false,
compressIfPossible = false,
).getOrThrow()
assertThat(result.file.path).endsWith("image.png")
val info = (result as MediaUploadInfo.Image)
assertThat(info.thumbnailFile).isNotNull()
assertThat(info.imageInfo).isEqualTo(
ImageInfo(
height = 1_178,
width = 1_818,
mimetype = MimeTypes.Png,
size = 1_856_786,
thumbnailInfo = ThumbnailInfo(height = 25, width = 25, mimetype = MimeTypes.Jpeg, size = 643),
thumbnailSource = null,
blurhash = "K00000fQfQfQfQfQfQfQfQ",
)
)
assertThat(file.exists()).isTrue()
}
@Test
fun `test processing image and delete`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val sut = createAndroidMediaPreProcessor(context)
val file = getFileFromAssets(context, "image.png")
val result = sut.process(
uri = file.toUri(),
mimeType = MimeTypes.Png,
deleteOriginal = true,
compressIfPossible = true,
).getOrThrow()
assertThat(result.file.path).endsWith("image.png")
val info = (result as MediaUploadInfo.Image)
assertThat(info.thumbnailFile).isNull() // TODO Check this
assertThat(info.imageInfo).isEqualTo(
ImageInfo(
height = 1_178,
width = 1_818,
mimetype = MimeTypes.Png,
size = 114_867,
thumbnailInfo = null,
thumbnailSource = null,
blurhash = null,
)
)
// Does not work
// assertThat(file.exists()).isFalse()
}
@Test
fun `test processing gif`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val sut = createAndroidMediaPreProcessor(context)
val file = getFileFromAssets(context, "animated_gif.gif")
val result = sut.process(
uri = file.toUri(),
mimeType = MimeTypes.Gif,
deleteOriginal = false,
compressIfPossible = true,
).getOrThrow()
assertThat(result.file.path).endsWith("animated_gif.gif")
val info = (result as MediaUploadInfo.Image)
assertThat(info.thumbnailFile).isNotNull()
assertThat(info.imageInfo).isEqualTo(
ImageInfo(
height = 600,
width = 800,
mimetype = MimeTypes.Gif,
size = 687979,
thumbnailInfo = ThumbnailInfo(height = 50, width = 50, mimetype = MimeTypes.Jpeg, size = 691),
thumbnailSource = null,
blurhash = "K00000fQfQfQfQfQfQfQfQ",
)
)
assertThat(file.exists()).isTrue()
}
@Test
fun `test processing file`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val sut = createAndroidMediaPreProcessor(context)
val file = getFileFromAssets(context, "text.txt")
val result = sut.process(
uri = file.toUri(),
mimeType = MimeTypes.PlainText,
deleteOriginal = false,
compressIfPossible = true,
).getOrThrow()
assertThat(result.file.path).endsWith("text.txt")
val info = (result as MediaUploadInfo.AnyFile)
assertThat(info.fileInfo).isEqualTo(
FileInfo(
mimetype = MimeTypes.PlainText,
size = 13,
thumbnailInfo = null,
thumbnailSource = null,
)
)
assertThat(file.exists()).isTrue()
}
@Test
fun `test processing video`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val sut = createAndroidMediaPreProcessor(context)
val file = getFileFromAssets(context, "video.mp4")
val result = sut.process(
uri = file.toUri(),
mimeType = MimeTypes.Mp4,
deleteOriginal = false,
compressIfPossible = true,
).getOrThrow()
assertThat(result.file.path).endsWith("video.mp4")
val info = (result as MediaUploadInfo.Video)
assertThat(info.thumbnailFile).isNotNull()
assertThat(info.videoInfo).isEqualTo(
VideoInfo(
duration = Duration.ZERO, // Not available with Robolectric?
height = 1_178,
width = 1_818,
mimetype = MimeTypes.Mp4,
size = 114_867,
thumbnailInfo = null,
thumbnailSource = null,
blurhash = null,
)
)
assertThat(file.exists()).isTrue()
}
@Test
fun `test processing video no compression`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val sut = createAndroidMediaPreProcessor(context)
val file = getFileFromAssets(context, "video.mp4")
val result = sut.process(
uri = file.toUri(),
mimeType = MimeTypes.Mp4,
deleteOriginal = false,
compressIfPossible = false,
).getOrThrow()
assertThat(result.file.path).endsWith("video.mp4")
val info = (result as MediaUploadInfo.Video)
assertThat(info.thumbnailFile).isNotNull()
assertThat(info.videoInfo).isEqualTo(
VideoInfo(
duration = Duration.ZERO, // Not available with Robolectric?
height = 0, // Not available with Robolectric?
width = 0, // Not available with Robolectric?
mimetype = MimeTypes.Mp4,
size = 1_673_712,
thumbnailInfo = ThumbnailInfo(height = null, width = null, mimetype = MimeTypes.Jpeg, size = 0), // Not available with Robolectric?
thumbnailSource = null,
blurhash = null,
)
)
assertThat(file.exists()).isTrue()
}
@Test
fun `test processing audio`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val sut = createAndroidMediaPreProcessor(context)
val file = getFileFromAssets(context, "sample3s.mp3")
val result = sut.process(
uri = file.toUri(),
mimeType = MimeTypes.Mp3,
deleteOriginal = false,
compressIfPossible = true,
).getOrThrow()
assertThat(result.file.path).endsWith("sample3s.mp3")
val info = (result as MediaUploadInfo.Audio)
assertThat(info.audioInfo).isEqualTo(
AudioInfo(
duration = Duration.ZERO, // Not available with Robolectric?
size = 52_079,
mimetype = MimeTypes.Mp3,
)
)
assertThat(file.exists()).isTrue()
}
@Test
fun `test file which does not exist`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val sut = createAndroidMediaPreProcessor(context)
val file = File(context.cacheDir, "not found.txt")
val result = sut.process(
uri = file.toUri(),
mimeType = MimeTypes.PlainText,
deleteOriginal = false,
compressIfPossible = true,
)
assertThat(result.isFailure).isTrue()
val failure = result.exceptionOrNull()
assertThat(failure).isInstanceOf(MediaPreProcessor.Failure::class.java)
assertThat(failure?.cause).isInstanceOf(FileNotFoundException::class.java)
}
private fun TestScope.createAndroidMediaPreProcessor(
context: Context,
sdkIntVersion: Int = Build.VERSION_CODES.P
) = AndroidMediaPreProcessor(
context = context,
thumbnailFactory = ThumbnailFactory(context, FakeBuildVersionSdkIntProvider(sdkIntVersion)),
imageCompressor = ImageCompressor(context, testCoroutineDispatchers()),
videoCompressor = VideoCompressor(context),
coroutineDispatchers = testCoroutineDispatchers(),
)
@Throws(IOException::class)
private fun getFileFromAssets(context: Context, fileName: String): File = File(context.cacheDir, fileName)
.also {
if (!it.exists()) {
it.outputStream().use { cache ->
context.assets.open(fileName).use { inputStream ->
inputStream.copyTo(cache)
}
}
}
}
}
Loading…
Cancel
Save