mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 21:32:34 +00:00
Cover the branches left untested by the uri short circuit
An intent carrying uris is now accepted before the mime type list is reached, so the image check that every uri share used to exercise is only covered by an intent which has nothing to send. Add one case per listed mime type without a uri. Cover the rest of the handler while it is under test: an intent with no resolvable type, a send multiple without a stream, an action which is neither send nor send multiple, an empty caption, a plain text intent without text, the mime type reported by the content resolver for the uri, and the explicit intent built for the application which will read it, both when the read permission can be granted and when it cannot.
This commit is contained in:
+191
@@ -7,15 +7,26 @@
|
||||
|
||||
package io.element.android.features.share.impl
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.ContentProvider
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import com.google.common.truth.Truth.assertWithMessage
|
||||
import io.element.android.features.share.api.ShareIntentData
|
||||
import io.element.android.features.share.api.UriToShare
|
||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||
import io.element.android.tests.testutils.robolectric.RobolectricTest
|
||||
import org.junit.Test
|
||||
import org.robolectric.Robolectric
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.Shadows.shadowOf
|
||||
|
||||
class DefaultShareIntentHandlerTest : RobolectricTest() {
|
||||
@Test
|
||||
@@ -101,12 +112,192 @@ class DefaultShareIntentHandlerTest : RobolectricTest() {
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an intent without mime type is not handled`() {
|
||||
val result = createDefaultShareIntentHandler().handleIncomingShareIntent(Intent(Intent.ACTION_SEND))
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an intent with a listed mime type and no uri is handled`() {
|
||||
listOf(
|
||||
MimeTypes.Jpeg,
|
||||
MimeTypes.Mp4,
|
||||
MimeTypes.Mp3,
|
||||
MimeTypes.Pdf,
|
||||
"file/binary",
|
||||
"text/html",
|
||||
MimeTypes.Any,
|
||||
).forEach { type ->
|
||||
val result = createDefaultShareIntentHandler().handleIncomingShareIntent(
|
||||
Intent(Intent.ACTION_SEND).apply { this.type = type }
|
||||
)
|
||||
assertWithMessage("$type was not properly handled").that(result).isEqualTo(
|
||||
ShareIntentData.Uris(
|
||||
text = null,
|
||||
uris = emptyList(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a send multiple intent without uri is handled`() {
|
||||
val intent = Intent(Intent.ACTION_SEND_MULTIPLE).apply {
|
||||
type = MimeTypes.Jpeg
|
||||
}
|
||||
val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent)
|
||||
assertThat(result).isEqualTo(
|
||||
ShareIntentData.Uris(
|
||||
text = null,
|
||||
uris = emptyList(),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an uri is ignored when the intent is neither a send nor a send multiple`() {
|
||||
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||
type = MimeTypes.Jpeg
|
||||
putExtra(Intent.EXTRA_STREAM, "content://sender/image.jpg".toUri())
|
||||
}
|
||||
val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent)
|
||||
assertThat(result).isEqualTo(
|
||||
ShareIntentData.Uris(
|
||||
text = null,
|
||||
uris = emptyList(),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `an empty text is not kept as a caption`() {
|
||||
val uri = "content://sender/logs.zip".toUri()
|
||||
val result = createDefaultShareIntentHandler().handleIncomingShareIntent(
|
||||
aSendIntent(type = "message/rfc822", uri = uri).apply {
|
||||
putExtra(Intent.EXTRA_TEXT, "")
|
||||
}
|
||||
)
|
||||
assertThat(result).isEqualTo(
|
||||
ShareIntentData.Uris(
|
||||
text = null,
|
||||
uris = listOf(UriToShare(uri = uri, mimeType = "message/rfc822")),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a plain text intent without text is not handled`() {
|
||||
val intent = Intent(Intent.ACTION_SEND).apply {
|
||||
type = MimeTypes.PlainText
|
||||
}
|
||||
val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent)
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a plain text intent with an empty text is not handled`() {
|
||||
val intent = Intent(Intent.ACTION_SEND).apply {
|
||||
type = MimeTypes.PlainText
|
||||
putExtra(Intent.EXTRA_TEXT, "")
|
||||
}
|
||||
val result = createDefaultShareIntentHandler().handleIncomingShareIntent(intent)
|
||||
assertThat(result).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `the mime type of the uri overrides the one of the intent`() {
|
||||
Robolectric.setupContentProvider(ZipContentProvider::class.java, "sender")
|
||||
val uri = "content://sender/logs.zip".toUri()
|
||||
val result = createDefaultShareIntentHandler().handleIncomingShareIntent(
|
||||
aSendIntent(type = MimeTypes.Any, uri = uri)
|
||||
)
|
||||
assertThat(result).isEqualTo(
|
||||
ShareIntentData.Uris(
|
||||
text = null,
|
||||
uris = listOf(UriToShare(uri = uri, mimeType = "application/zip")),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `the intent is made explicit for the application which will read the uri`() {
|
||||
val intent = aSendIntent(type = "message/rfc822", uri = "content://sender/logs.zip".toUri())
|
||||
givenAnApplicationHandling(intent)
|
||||
createDefaultShareIntentHandler().handleIncomingShareIntent(intent)
|
||||
assertThat(intent.action).isNull()
|
||||
assertThat(intent.component).isEqualTo(ComponentName(A_PACKAGE_NAME, AN_ACTIVITY_NAME))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `the intent stays implicit when the uri permission cannot be granted`() {
|
||||
val uri = "content://sender/logs.zip".toUri()
|
||||
val intent = aSendIntent(type = "message/rfc822", uri = uri)
|
||||
givenAnApplicationHandling(intent)
|
||||
val result = DefaultShareIntentHandler(
|
||||
context = FailingGrantUriPermissionContext(RuntimeEnvironment.getApplication()),
|
||||
).handleIncomingShareIntent(intent)
|
||||
assertThat(intent.action).isEqualTo(Intent.ACTION_SEND)
|
||||
assertThat(intent.component).isNull()
|
||||
assertThat(result).isEqualTo(
|
||||
ShareIntentData.Uris(
|
||||
text = null,
|
||||
uris = listOf(UriToShare(uri = uri, mimeType = "message/rfc822")),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun aSendIntent(type: String, uri: Uri) = Intent(Intent.ACTION_SEND).apply {
|
||||
this.type = type
|
||||
putExtra(Intent.EXTRA_STREAM, uri)
|
||||
}
|
||||
|
||||
private fun givenAnApplicationHandling(intent: Intent) {
|
||||
val componentName = ComponentName(A_PACKAGE_NAME, AN_ACTIVITY_NAME)
|
||||
shadowOf(RuntimeEnvironment.getApplication().packageManager).apply {
|
||||
addActivityIfNotPresent(componentName)
|
||||
addIntentFilterForActivity(
|
||||
componentName,
|
||||
IntentFilter(intent.action).apply {
|
||||
addCategory(Intent.CATEGORY_DEFAULT)
|
||||
addDataType(intent.type)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createDefaultShareIntentHandler() = DefaultShareIntentHandler(
|
||||
context = RuntimeEnvironment.getApplication(),
|
||||
)
|
||||
|
||||
private class FailingGrantUriPermissionContext(context: Context) : ContextWrapper(context) {
|
||||
override fun grantUriPermission(toPackage: String, uri: Uri, modeFlags: Int) {
|
||||
error("Unable to grant Uri permission")
|
||||
}
|
||||
}
|
||||
|
||||
private class ZipContentProvider : ContentProvider() {
|
||||
override fun onCreate() = true
|
||||
|
||||
override fun getType(uri: Uri) = "application/zip"
|
||||
|
||||
override fun query(
|
||||
uri: Uri,
|
||||
projection: Array<out String>?,
|
||||
selection: String?,
|
||||
selectionArgs: Array<out String>?,
|
||||
sortOrder: String?,
|
||||
): Cursor? = null
|
||||
|
||||
override fun insert(uri: Uri, values: ContentValues?): Uri? = null
|
||||
|
||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?) = 0
|
||||
|
||||
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<out String>?) = 0
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val A_PACKAGE_NAME = "io.element.android.x"
|
||||
const val AN_ACTIVITY_NAME = "io.element.android.x.MainActivity"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user