NodeInfo: replace Int by Long in upload limits, add test cases
This commit is contained in:
parent
25b57f3db4
commit
930e05be27
|
@ -122,7 +122,8 @@ class ComposeActivity : BaseActivity(),
|
||||||
var maximumTootCharacters = DEFAULT_CHARACTER_LIMIT
|
var maximumTootCharacters = DEFAULT_CHARACTER_LIMIT
|
||||||
|
|
||||||
private var composeOptions: ComposeOptions? = null
|
private var composeOptions: ComposeOptions? = null
|
||||||
private val viewModel: ComposeViewModel by viewModels { viewModelFactory }
|
@VisibleForTesting
|
||||||
|
val viewModel: ComposeViewModel by viewModels { viewModelFactory }
|
||||||
private var suggestFormattingSyntax: String = "text/markdown"
|
private var suggestFormattingSyntax: String = "text/markdown"
|
||||||
|
|
||||||
private var mediaCount = 0
|
private var mediaCount = 0
|
||||||
|
@ -347,7 +348,8 @@ class ComposeActivity : BaseActivity(),
|
||||||
enablePollButton(true)
|
enablePollButton(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var supportedFormattingSyntax = arrayListOf<String>()
|
@VisibleForTesting
|
||||||
|
var supportedFormattingSyntax = arrayListOf<String>()
|
||||||
|
|
||||||
private fun subscribeToUpdates(mediaAdapter: MediaPreviewAdapter) {
|
private fun subscribeToUpdates(mediaAdapter: MediaPreviewAdapter) {
|
||||||
withLifecycleContext {
|
withLifecycleContext {
|
||||||
|
|
|
@ -560,8 +560,8 @@ fun <T> mutableLiveData(default: T) = MutableLiveData<T>().apply { value = defau
|
||||||
const val DEFAULT_CHARACTER_LIMIT = 500
|
const val DEFAULT_CHARACTER_LIMIT = 500
|
||||||
private const val DEFAULT_MAX_OPTION_COUNT = 4
|
private const val DEFAULT_MAX_OPTION_COUNT = 4
|
||||||
private const val DEFAULT_MAX_OPTION_LENGTH = 25
|
private const val DEFAULT_MAX_OPTION_LENGTH = 25
|
||||||
private const val STATUS_VIDEO_SIZE_LIMIT = 41943040 // 40MiB
|
private const val STATUS_VIDEO_SIZE_LIMIT : Long = 41943040 // 40MiB
|
||||||
private const val STATUS_IMAGE_SIZE_LIMIT = 8388608 // 8MiB
|
private const val STATUS_IMAGE_SIZE_LIMIT : Long = 8388608 // 8MiB
|
||||||
|
|
||||||
|
|
||||||
data class ComposeInstanceParams(
|
data class ComposeInstanceParams(
|
||||||
|
@ -576,6 +576,6 @@ data class ComposeInstanceMetadata(
|
||||||
val supportsMarkdown: Boolean,
|
val supportsMarkdown: Boolean,
|
||||||
val supportsBBcode: Boolean,
|
val supportsBBcode: Boolean,
|
||||||
val supportsHTML: Boolean,
|
val supportsHTML: Boolean,
|
||||||
val videoLimit: Int,
|
val videoLimit: Long,
|
||||||
val imageLimit: Int
|
val imageLimit: Long
|
||||||
)
|
)
|
||||||
|
|
|
@ -75,7 +75,7 @@ public class DownsizeImageTask extends AsyncTask<Uri, Void, Boolean> {
|
||||||
super.onPostExecute(successful);
|
super.onPostExecute(successful);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean resize(Uri[] uris, int sizeLimit, ContentResolver contentResolver,
|
public static boolean resize(Uri[] uris, long sizeLimit, ContentResolver contentResolver,
|
||||||
File tempFile) {
|
File tempFile) {
|
||||||
for (Uri uri : uris) {
|
for (Uri uri : uris) {
|
||||||
InputStream inputStream;
|
InputStream inputStream;
|
||||||
|
|
|
@ -59,8 +59,8 @@ fun createNewImageFile(context: Context): File {
|
||||||
data class PreparedMedia(val type: Int, val uri: Uri, val size: Long)
|
data class PreparedMedia(val type: Int, val uri: Uri, val size: Long)
|
||||||
|
|
||||||
interface MediaUploader {
|
interface MediaUploader {
|
||||||
fun prepareMedia(inUri: Uri, videoLimit: Int, imageLimit: Int, filename: String?): Single<PreparedMedia>
|
fun prepareMedia(inUri: Uri, videoLimit: Long, imageLimit: Long, filename: String?): Single<PreparedMedia>
|
||||||
fun uploadMedia(media: QueuedMedia, videoLimit: Int, imageLimit: Int): Observable<UploadEvent>
|
fun uploadMedia(media: QueuedMedia, videoLimit: Long, imageLimit: Long): Observable<UploadEvent>
|
||||||
}
|
}
|
||||||
|
|
||||||
class AudioSizeException : Exception()
|
class AudioSizeException : Exception()
|
||||||
|
@ -73,14 +73,14 @@ class MediaUploaderImpl(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val mastodonApi: MastodonApi
|
private val mastodonApi: MastodonApi
|
||||||
) : MediaUploader {
|
) : MediaUploader {
|
||||||
override fun uploadMedia(media: QueuedMedia, videoLimit: Int, imageLimit: Int): Observable<UploadEvent> {
|
override fun uploadMedia(media: QueuedMedia, videoLimit: Long, imageLimit: Long): Observable<UploadEvent> {
|
||||||
return Observable
|
return Observable
|
||||||
.fromCallable {
|
.fromCallable {
|
||||||
if (shouldResizeMedia(media, imageLimit)) {
|
if (shouldResizeMedia(media, imageLimit)) {
|
||||||
downsize(media, imageLimit)
|
downsize(media, imageLimit)
|
||||||
} else media
|
} else media
|
||||||
}
|
}
|
||||||
.switchMap { upload(it, videoLimit, imageLimit) }
|
.switchMap { upload(it) }
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@ class MediaUploaderImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun prepareMedia(inUri: Uri, videoLimit: Int, imageLimit: Int, filename: String?): Single<PreparedMedia> {
|
override fun prepareMedia(inUri: Uri, videoLimit: Long, imageLimit: Long, filename: String?): Single<PreparedMedia> {
|
||||||
return Single.fromCallable {
|
return Single.fromCallable {
|
||||||
var mediaSize = getMediaSize(contentResolver, inUri)
|
var mediaSize = getMediaSize(contentResolver, inUri)
|
||||||
var uri = inUri
|
var uri = inUri
|
||||||
|
@ -159,7 +159,7 @@ class MediaUploaderImpl(
|
||||||
|
|
||||||
private val contentResolver = context.contentResolver
|
private val contentResolver = context.contentResolver
|
||||||
|
|
||||||
private fun upload(media: QueuedMedia, videoLimit: Int, imageLimit: Int): Observable<UploadEvent> {
|
private fun upload(media: QueuedMedia): Observable<UploadEvent> {
|
||||||
return Observable.create { emitter ->
|
return Observable.create { emitter ->
|
||||||
var (mimeType, fileExtension) = getMimeTypeAndSuffixFromFilenameOrUri(media.uri, media.originalFileName)
|
var (mimeType, fileExtension) = getMimeTypeAndSuffixFromFilenameOrUri(media.uri, media.originalFileName)
|
||||||
val filename = String.format("%s_%s_%s%s",
|
val filename = String.format("%s_%s_%s%s",
|
||||||
|
@ -196,13 +196,13 @@ class MediaUploaderImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downsize(media: QueuedMedia, imageLimit: Int): QueuedMedia {
|
private fun downsize(media: QueuedMedia, imageLimit: Long): QueuedMedia {
|
||||||
val file = createNewImageFile(context)
|
val file = createNewImageFile(context)
|
||||||
DownsizeImageTask.resize(arrayOf(media.uri), imageLimit, context.contentResolver, file)
|
DownsizeImageTask.resize(arrayOf(media.uri), imageLimit, context.contentResolver, file)
|
||||||
return media.copy(uri = file.toUri(), mediaSize = file.length())
|
return media.copy(uri = file.toUri(), mediaSize = file.length())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun shouldResizeMedia(media: QueuedMedia, imageLimit: Int): Boolean {
|
private fun shouldResizeMedia(media: QueuedMedia, imageLimit: Long): Boolean {
|
||||||
// resize only images
|
// resize only images
|
||||||
if(media.type == QueuedMedia.Type.IMAGE) {
|
if(media.type == QueuedMedia.Type.IMAGE) {
|
||||||
// resize when exceed image limit
|
// resize when exceed image limit
|
||||||
|
|
|
@ -25,7 +25,7 @@ data class NodeInfoLink(
|
||||||
)
|
)
|
||||||
|
|
||||||
data class NodeInfoLinks(
|
data class NodeInfoLinks(
|
||||||
val links: ArrayList<NodeInfoLink>
|
val links: List<NodeInfoLink>
|
||||||
)
|
)
|
||||||
|
|
||||||
// we care only about supported postFormats
|
// we care only about supported postFormats
|
||||||
|
@ -41,14 +41,14 @@ data class NodeInfoSoftware(
|
||||||
)
|
)
|
||||||
|
|
||||||
data class NodeInfoPleromaUploadLimits(
|
data class NodeInfoPleromaUploadLimits(
|
||||||
val avatar: Int?,
|
val avatar: Long?,
|
||||||
val background: Int?,
|
val background: Long?,
|
||||||
val banner: Int?,
|
val banner: Long?,
|
||||||
val general: Int?
|
val general: Long?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class NodeInfoPixelfedUploadLimits(
|
data class NodeInfoPixelfedUploadLimits(
|
||||||
@SerializedName("max_photo_size") val maxPhotoSize: Int?
|
@SerializedName("max_photo_size") val maxPhotoSize: Long?
|
||||||
)
|
)
|
||||||
|
|
||||||
data class NodeInfoPixelfedConfig(
|
data class NodeInfoPixelfedConfig(
|
||||||
|
@ -56,7 +56,7 @@ data class NodeInfoPixelfedConfig(
|
||||||
)
|
)
|
||||||
|
|
||||||
data class NodeInfoMetadata(
|
data class NodeInfoMetadata(
|
||||||
val postFormats: ArrayList<String>?,
|
val postFormats: List<String>?,
|
||||||
val uploadLimits: NodeInfoPleromaUploadLimits?,
|
val uploadLimits: NodeInfoPleromaUploadLimits?,
|
||||||
val config: NodeInfoPixelfedConfig?
|
val config: NodeInfoPixelfedConfig?
|
||||||
)
|
)
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.mockito.Mockito.mock
|
||||||
import org.robolectric.Robolectric
|
import org.robolectric.Robolectric
|
||||||
import org.robolectric.annotation.Config
|
import org.robolectric.annotation.Config
|
||||||
import org.robolectric.fakes.RoboMenuItem
|
import org.robolectric.fakes.RoboMenuItem
|
||||||
|
import java.lang.Math.pow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by charlag on 3/7/18.
|
* Created by charlag on 3/7/18.
|
||||||
|
@ -75,9 +76,7 @@ class ComposeActivityTest {
|
||||||
notificationLight = true
|
notificationLight = true
|
||||||
)
|
)
|
||||||
var instanceResponseCallback: (()->Instance)? = null
|
var instanceResponseCallback: (()->Instance)? = null
|
||||||
private val nodeinfoLinks = NodeInfoLinks(
|
var nodeinfoResponseCallback: (()->NodeInfo)? = null
|
||||||
links = arrayListOf<NodeInfoLink>()
|
|
||||||
)
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setupActivity() {
|
fun setupActivity() {
|
||||||
|
@ -89,7 +88,27 @@ class ComposeActivityTest {
|
||||||
|
|
||||||
apiMock = mock(MastodonApi::class.java)
|
apiMock = mock(MastodonApi::class.java)
|
||||||
`when`(apiMock.getCustomEmojis()).thenReturn(Single.just(emptyList()))
|
`when`(apiMock.getCustomEmojis()).thenReturn(Single.just(emptyList()))
|
||||||
`when`(apiMock.getNodeinfoLinks()).thenReturn(Single.just(nodeinfoLinks))
|
`when`(apiMock.getNodeinfoLinks()).thenReturn(object: Single<NodeInfoLinks>() {
|
||||||
|
override fun subscribeActual(observer: SingleObserver<in NodeInfoLinks>) {
|
||||||
|
if (nodeinfoResponseCallback == null) {
|
||||||
|
observer.onError(Throwable())
|
||||||
|
} else {
|
||||||
|
observer.onSuccess(NodeInfoLinks(
|
||||||
|
listOf( NodeInfoLink( "", "" ) )
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
`when`(apiMock.getNodeinfo("")).thenReturn(object: Single<NodeInfo>() {
|
||||||
|
override fun subscribeActual(observer: SingleObserver< in NodeInfo>) {
|
||||||
|
val nodeinfo = nodeinfoResponseCallback?.invoke()
|
||||||
|
if (nodeinfo == null) {
|
||||||
|
observer.onError(Throwable())
|
||||||
|
} else {
|
||||||
|
observer.onSuccess(nodeinfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
`when`(apiMock.getInstance()).thenReturn(object: Single<Instance>() {
|
`when`(apiMock.getInstance()).thenReturn(object: Single<Instance>() {
|
||||||
override fun subscribeActual(observer: SingleObserver<in Instance>) {
|
override fun subscribeActual(observer: SingleObserver<in Instance>) {
|
||||||
val instance = instanceResponseCallback?.invoke()
|
val instance = instanceResponseCallback?.invoke()
|
||||||
|
@ -164,12 +183,66 @@ class ComposeActivityTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun whenMaximumTootCharsIsPopulated_customLimitIsUsed() {
|
fun whenMaximumTootCharsIsPopulated_customLimitIsUsed() {
|
||||||
val customMaximum = 1000
|
val customMaximum = 2147483647
|
||||||
instanceResponseCallback = { getInstanceWithMaximumTootCharacters(customMaximum) }
|
instanceResponseCallback = { getInstanceWithMaximumTootCharacters(customMaximum) }
|
||||||
setupActivity()
|
setupActivity()
|
||||||
assertEquals(customMaximum, activity.maximumTootCharacters)
|
assertEquals(customMaximum, activity.maximumTootCharacters)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenPleromaInNodeinfo_attachmentLimitsRemoved() {
|
||||||
|
nodeinfoResponseCallback = { getPleromaNodeinfo(
|
||||||
|
null,
|
||||||
|
NodeInfoPleromaUploadLimits( 100, 100, 100, 100 ))
|
||||||
|
}
|
||||||
|
setupActivity()
|
||||||
|
assertEquals(true, activity.viewModel.hasNoAttachmentLimits)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenPleromaInNodeinfo_haveFormatting() {
|
||||||
|
nodeinfoResponseCallback = { getPleromaNodeinfo(
|
||||||
|
listOf("text/plain", "text/markdown", "text/bbcode"),
|
||||||
|
NodeInfoPleromaUploadLimits( 100, 100, 100, 100 ))
|
||||||
|
}
|
||||||
|
setupActivity()
|
||||||
|
assertArrayEquals(arrayOf("text/markdown", "text/bbcode"), activity.supportedFormattingSyntax.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenPleromaInNodeinfo_haveCustomUploadLimits() {
|
||||||
|
nodeinfoResponseCallback = { getPleromaNodeinfo(
|
||||||
|
null,
|
||||||
|
NodeInfoPleromaUploadLimits( Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE ))
|
||||||
|
}
|
||||||
|
setupActivity()
|
||||||
|
assertEquals(Long.MAX_VALUE, activity.viewModel.instanceMetadata.value!!.imageLimit)
|
||||||
|
assertEquals(Long.MAX_VALUE, activity.viewModel.instanceMetadata.value!!.videoLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenPixelfedInNodeInfo_haveCustomUploadLimits() {
|
||||||
|
nodeinfoResponseCallback = { getPixelfedNodeinfo( 1024 * 1024 ) }
|
||||||
|
setupActivity()
|
||||||
|
assertEquals(1024 * 1024 * 1024, activity.viewModel.instanceMetadata.value!!.imageLimit)
|
||||||
|
assertEquals(1024 * 1024 * 1024, activity.viewModel.instanceMetadata.value!!.videoLimit)
|
||||||
|
assertArrayEquals(emptyArray(), activity.supportedFormattingSyntax.toTypedArray()) // pixelfed has no formatting
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenMastodonInNodeinfo_butItsAGlitch() {
|
||||||
|
nodeinfoResponseCallback = { getMastodonNodeinfo( "3.1.0+glitch" ) }
|
||||||
|
setupActivity()
|
||||||
|
assertArrayEquals(arrayOf("text/markdown", "text/html"), activity.supportedFormattingSyntax.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun whenMastodonInNodeinfo_butItsBoringVanilla() {
|
||||||
|
nodeinfoResponseCallback = { getMastodonNodeinfo( "3.1.0" ) }
|
||||||
|
setupActivity()
|
||||||
|
assertArrayEquals(emptyArray(), activity.supportedFormattingSyntax.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun whenTextContainsNoUrl_everyCharacterIsCounted() {
|
fun whenTextContainsNoUrl_everyCharacterIsCounted() {
|
||||||
val content = "This is test content please ignore thx "
|
val content = "This is test content please ignore thx "
|
||||||
|
@ -361,6 +434,43 @@ class ComposeActivityTest {
|
||||||
activity.findViewById<EditText>(R.id.composeEditField).setText(text ?: "Some text")
|
activity.findViewById<EditText>(R.id.composeEditField).setText(text ?: "Some text")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getPleromaNodeinfo(postFormats: List<String>?, limits: NodeInfoPleromaUploadLimits) : NodeInfo
|
||||||
|
{
|
||||||
|
return NodeInfo(
|
||||||
|
NodeInfoMetadata(
|
||||||
|
postFormats,
|
||||||
|
limits,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
NodeInfoSoftware(
|
||||||
|
"pleroma",
|
||||||
|
"2.0.0"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getPixelfedNodeinfo(maxPhotoSize: Long) : NodeInfo {
|
||||||
|
return NodeInfo(
|
||||||
|
NodeInfoMetadata(
|
||||||
|
null, null, NodeInfoPixelfedConfig( NodeInfoPixelfedUploadLimits( maxPhotoSize ) )
|
||||||
|
),
|
||||||
|
NodeInfoSoftware(
|
||||||
|
"pixelfed",
|
||||||
|
"2.0.0"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMastodonNodeinfo(version: String) : NodeInfo {
|
||||||
|
return NodeInfo(
|
||||||
|
null,
|
||||||
|
NodeInfoSoftware(
|
||||||
|
"mastodon",
|
||||||
|
version
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getInstanceWithMaximumTootCharacters(maximumTootCharacters: Int?): Instance
|
private fun getInstanceWithMaximumTootCharacters(maximumTootCharacters: Int?): Instance
|
||||||
{
|
{
|
||||||
return Instance(
|
return Instance(
|
||||||
|
|
Loading…
Reference in New Issue