diff --git a/briar-android/src/androidTestOfficial/assets/animated.gif b/briar-android/src/androidTestOfficial/assets/animated.gif new file mode 100644 index 000000000..1962fbfb5 Binary files /dev/null and b/briar-android/src/androidTestOfficial/assets/animated.gif differ diff --git a/briar-android/src/androidTestOfficial/assets/animated2.gif b/briar-android/src/androidTestOfficial/assets/animated2.gif new file mode 100644 index 000000000..46cfc5df7 Binary files /dev/null and b/briar-android/src/androidTestOfficial/assets/animated2.gif differ diff --git a/briar-android/src/androidTestOfficial/assets/error_high.jpg b/briar-android/src/androidTestOfficial/assets/error_high.jpg new file mode 100644 index 000000000..e2511cb26 Binary files /dev/null and b/briar-android/src/androidTestOfficial/assets/error_high.jpg differ diff --git a/briar-android/src/androidTestOfficial/assets/error_large.gif b/briar-android/src/androidTestOfficial/assets/error_large.gif new file mode 100644 index 000000000..c3d431741 Binary files /dev/null and b/briar-android/src/androidTestOfficial/assets/error_large.gif differ diff --git a/briar-android/src/androidTestOfficial/assets/error_wide.jpg b/briar-android/src/androidTestOfficial/assets/error_wide.jpg new file mode 100644 index 000000000..c547e21ee Binary files /dev/null and b/briar-android/src/androidTestOfficial/assets/error_wide.jpg differ diff --git a/briar-android/src/androidTestOfficial/java/org/briarproject/briar/android/conversation/AttachmentControllerIntegrationTest.java b/briar-android/src/androidTestOfficial/java/org/briarproject/briar/android/conversation/AttachmentControllerIntegrationTest.java new file mode 100644 index 000000000..d639230b8 --- /dev/null +++ b/briar-android/src/androidTestOfficial/java/org/briarproject/briar/android/conversation/AttachmentControllerIntegrationTest.java @@ -0,0 +1,279 @@ +package org.briarproject.briar.android.conversation; + +import android.content.res.AssetManager; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import org.briarproject.bramble.api.UniqueId; +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.briar.api.messaging.Attachment; +import org.briarproject.briar.api.messaging.AttachmentHeader; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Random; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(AndroidJUnit4.class) +public class AttachmentControllerIntegrationTest { + + private static final String smallKitten = + "https://upload.wikimedia.org/wikipedia/commons/thumb/0/06/Kitten_in_Rizal_Park%2C_Manila.jpg/160px-Kitten_in_Rizal_Park%2C_Manila.jpg"; + private static final String originalKitten = + "https://upload.wikimedia.org/wikipedia/commons/0/06/Kitten_in_Rizal_Park%2C_Manila.jpg"; + private static final String pngKitten = + "https://upload.wikimedia.org/wikipedia/commons/c/c8/Young_cat.png"; + private static final String uberGif = + "https://raw.githubusercontent.com/fuzzdb-project/fuzzdb/master/attack/file-upload/malicious-images/uber.gif"; + private static final String lottaPixel = + "https://raw.githubusercontent.com/fuzzdb-project/fuzzdb/master/attack/file-upload/malicious-images/lottapixel.jpg"; + private static final String imageIoCrash = + "https://www.landaire.net/img/crasher.png"; + private static final String gimpCrash = + "https://gitlab.gnome.org/GNOME/gimp/uploads/75f5b7ed3b09b3f1c13f1f65bffe784f/31153c919d3aa634e8e6cff82219fe7352dd8a37.png"; + private static final String optiPngAfl = + "https://sourceforge.net/p/optipng/bugs/64/attachment/test.gif"; + private static final String librawError = + "https://www.libraw.org/sites/libraw.org/files/P1010671.JPG"; + + private final AttachmentDimensions dimensions = new AttachmentDimensions( + 100, 50, 200, 75, 300 + ); + private final MessageId msgId = new MessageId(getRandomId()); + + private final AttachmentController controller = + new AttachmentController(null, dimensions); + + @Test + public void testSmallJpegImage() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); + InputStream is = getUrlInputStream(smallKitten); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(msgId, item.getMessageId()); + assertEquals(160, item.getWidth()); + assertEquals(240, item.getHeight()); + assertEquals(160, item.getThumbnailWidth()); + assertEquals(240, item.getThumbnailHeight()); + assertEquals("image/jpeg", item.getMimeType()); + assertEquals("jpg", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testBigJpegImage() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); + InputStream is = getUrlInputStream(originalKitten); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(msgId, item.getMessageId()); + assertEquals(1728, item.getWidth()); + assertEquals(2592, item.getHeight()); + assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); + assertEquals(dimensions.maxHeight, item.getThumbnailHeight()); + assertEquals("image/jpeg", item.getMimeType()); + assertEquals("jpg", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testSmallPngImage() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/png"); + InputStream is = getUrlInputStream(pngKitten); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(msgId, item.getMessageId()); + assertEquals(737, item.getWidth()); + assertEquals(510, item.getHeight()); + assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); + assertEquals(138, item.getThumbnailHeight()); + assertEquals("image/png", item.getMimeType()); + assertEquals("png", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testUberGif() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); + InputStream is = getUrlInputStream(uberGif); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(1, item.getWidth()); + assertEquals(1, item.getHeight()); + assertEquals(dimensions.minHeight, item.getThumbnailWidth()); + assertEquals(dimensions.minHeight, item.getThumbnailHeight()); + assertEquals("image/gif", item.getMimeType()); + assertEquals("gif", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testLottaPixels() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); + InputStream is = getUrlInputStream(lottaPixel); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(64250, item.getWidth()); + assertEquals(64250, item.getHeight()); + assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); + assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); + assertEquals("image/jpeg", item.getMimeType()); + assertEquals("jpg", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testImageIoCrash() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); + InputStream is = getUrlInputStream(imageIoCrash); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(1184, item.getWidth()); + assertEquals(448, item.getHeight()); + assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); + assertEquals(dimensions.minHeight, item.getThumbnailHeight()); + assertEquals("image/png", item.getMimeType()); + assertEquals("png", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testGimpCrash() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); + InputStream is = getUrlInputStream(gimpCrash); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(1, item.getWidth()); + assertEquals(1, item.getHeight()); + assertEquals(dimensions.minHeight, item.getThumbnailWidth()); + assertEquals(dimensions.minHeight, item.getThumbnailHeight()); + assertEquals("image/gif", item.getMimeType()); + assertEquals("gif", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testOptiPngAfl() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); + InputStream is = getUrlInputStream(optiPngAfl); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(32, item.getWidth()); + assertEquals(32, item.getHeight()); + assertEquals(dimensions.minHeight, item.getThumbnailWidth()); + assertEquals(dimensions.minHeight, item.getThumbnailHeight()); + assertEquals("image/gif", item.getMimeType()); + assertEquals("gif", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testLibrawError() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); + InputStream is = getUrlInputStream(librawError); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertTrue(item.hasError()); + } + + @Test + public void testSmallAnimatedGifMaxDimensions() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); + InputStream is = getAssetInputStream("animated.gif"); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(65535, item.getWidth()); + assertEquals(65535, item.getHeight()); + assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); + assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); + assertEquals("image/gif", item.getMimeType()); + assertEquals("gif", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testSmallAnimatedGifHugeDimensions() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); + InputStream is = getAssetInputStream("animated2.gif"); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(10000, item.getWidth()); + assertEquals(10000, item.getHeight()); + assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); + assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); + assertEquals("image/gif", item.getMimeType()); + assertEquals("gif", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testSmallGifLargeDimensions() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/gif"); + InputStream is = getAssetInputStream("error_large.gif"); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(16384, item.getWidth()); + assertEquals(16384, item.getHeight()); + assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); + assertEquals(dimensions.maxWidth, item.getThumbnailHeight()); + assertEquals("image/gif", item.getMimeType()); + assertEquals("gif", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testHighError() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); + InputStream is = getAssetInputStream("error_high.jpg"); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(1, item.getWidth()); + assertEquals(10000, item.getHeight()); + assertEquals(dimensions.minWidth, item.getThumbnailWidth()); + assertEquals(dimensions.maxHeight, item.getThumbnailHeight()); + assertEquals("image/jpeg", item.getMimeType()); + assertEquals("jpg", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testWideError() throws Exception { + AttachmentHeader h = new AttachmentHeader(msgId, "image/jpeg"); + InputStream is = getAssetInputStream("error_wide.jpg"); + Attachment a = new Attachment(is); + AttachmentItem item = controller.getAttachmentItem(h, a, true); + assertEquals(1920, item.getWidth()); + assertEquals(1, item.getHeight()); + assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); + assertEquals(dimensions.minHeight, item.getThumbnailHeight()); + assertEquals("image/jpeg", item.getMimeType()); + assertEquals("jpg", item.getExtension()); + assertFalse(item.hasError()); + } + + private InputStream getUrlInputStream(String url) throws IOException { + return new URL(url).openStream(); + } + + private InputStream getAssetInputStream(String name) throws IOException { + AssetManager assets = InstrumentationRegistry.getContext().getAssets(); + return assets.open(name); + } + + public static byte[] getRandomBytes(int length) { + byte[] b = new byte[length]; + new Random().nextBytes(b); + return b; + } + + public static byte[] getRandomId() { + return getRandomBytes(UniqueId.LENGTH); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentController.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentController.java index 3f1c55d6d..11833f2ed 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentController.java @@ -1,7 +1,7 @@ package org.briarproject.briar.android.conversation; -import android.content.res.Resources; import android.graphics.BitmapFactory; +import android.graphics.BitmapFactory.Options; import android.support.annotation.Nullable; import android.support.media.ExifInterface; import android.webkit.MimeTypeMap; @@ -13,7 +13,7 @@ import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.sync.MessageId; -import org.briarproject.briar.R; +import org.briarproject.briar.android.conversation.ImageHelper.DecodeResult; import org.briarproject.briar.api.messaging.Attachment; import org.briarproject.briar.api.messaging.AttachmentHeader; import org.briarproject.briar.api.messaging.MessagingManager; @@ -49,6 +49,7 @@ class AttachmentController { private static final int READ_LIMIT = 1024 * 8192; private final MessagingManager messagingManager; + private final ImageHelper imageHelper; private final int defaultSize; private final int minWidth, maxWidth; private final int minHeight, maxHeight; @@ -56,18 +57,38 @@ class AttachmentController { private final Map> attachmentCache = new ConcurrentHashMap<>(); - AttachmentController(MessagingManager messagingManager, Resources res) { + AttachmentController(MessagingManager messagingManager, + AttachmentDimensions dimensions, ImageHelper imageHelper) { this.messagingManager = messagingManager; - defaultSize = - res.getDimensionPixelSize(R.dimen.message_bubble_image_default); - minWidth = res.getDimensionPixelSize( - R.dimen.message_bubble_image_min_width); - maxWidth = res.getDimensionPixelSize( - R.dimen.message_bubble_image_max_width); - minHeight = res.getDimensionPixelSize( - R.dimen.message_bubble_image_min_height); - maxHeight = res.getDimensionPixelSize( - R.dimen.message_bubble_image_max_height); + this.imageHelper = imageHelper; + defaultSize = dimensions.defaultSize; + minWidth = dimensions.minWidth; + maxWidth = dimensions.maxWidth; + minHeight = dimensions.minHeight; + maxHeight = dimensions.maxHeight; + } + + AttachmentController(MessagingManager messagingManager, + AttachmentDimensions dimensions) { + this(messagingManager, dimensions, new ImageHelper() { + @Override + public DecodeResult decodeStream(InputStream is) { + Options options = new Options(); + options.inJustDecodeBounds = true; + BitmapFactory.decodeStream(is, null, options); + String mimeType = options.outMimeType; + if (mimeType == null) mimeType = ""; + return new DecodeResult(options.outWidth, options.outHeight, + mimeType); + } + + @Nullable + @Override + public String getExtensionFromMimeType(String mimeType) { + MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); + return mimeTypeMap.getExtensionFromMimeType(mimeType); + } + }); } void put(MessageId messageId, List attachments) { @@ -119,7 +140,7 @@ class AttachmentController { MessageId messageId = h.getMessageId(); if (!needsSize) { String mimeType = h.getContentType(); - String extension = getExtensionFromMimeType(mimeType); + String extension = imageHelper.getExtensionFromMimeType(mimeType); boolean hasError = false; if (extension == null) { extension = ""; @@ -162,7 +183,7 @@ class AttachmentController { getThumbnailSize(size.width, size.height, size.mimeType); } // get file extension - String extension = getExtensionFromMimeType(size.mimeType); + String extension = imageHelper.getExtensionFromMimeType(size.mimeType); boolean hasError = extension == null || size.error; if (extension == null) extension = ""; return new AttachmentItem(messageId, size.width, size.height, @@ -170,17 +191,10 @@ class AttachmentController { thumbnailSize.height, hasError); } - @Nullable - private String getExtensionFromMimeType(String mimeType) { - MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); - return mimeTypeMap.getExtensionFromMimeType(mimeType); - } - /** * Gets the size of a JPEG {@link InputStream} if EXIF info is available. */ - private static Size getSizeFromExif(InputStream is) - throws IOException { + private Size getSizeFromExif(InputStream is) throws IOException { ExifInterface exif = new ExifInterface(is); // these can return 0 independent of default value int width = exif.getAttributeInt(TAG_IMAGE_WIDTH, 0); @@ -200,14 +214,10 @@ class AttachmentController { /** * Gets the size of any image {@link InputStream}. */ - private static Size getSizeFromBitmap(InputStream is) { - BitmapFactory.Options options = new BitmapFactory.Options(); - options.inJustDecodeBounds = true; - BitmapFactory.decodeStream(is, null, options); - if (options.outWidth < 1 || options.outHeight < 1) - return new Size(); - return new Size(options.outWidth, options.outHeight, - options.outMimeType); + private Size getSizeFromBitmap(InputStream is) { + DecodeResult result = imageHelper.decodeStream(is); + if (result.width < 1 || result.height < 1) return new Size(); + return new Size(result.width, result.height, result.mimeType); } private Size getThumbnailSize(int width, int height, String mimeType) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentDimensions.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentDimensions.java new file mode 100644 index 000000000..081a6480b --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/AttachmentDimensions.java @@ -0,0 +1,39 @@ +package org.briarproject.briar.android.conversation; + +import android.content.res.Resources; +import android.support.annotation.VisibleForTesting; + +import org.briarproject.briar.R; + +class AttachmentDimensions { + + final int defaultSize; + final int minWidth, maxWidth; + final int minHeight, maxHeight; + + @VisibleForTesting + AttachmentDimensions(int defaultSize, int minWidth, int maxWidth, + int minHeight, int maxHeight) { + this.defaultSize = defaultSize; + this.minWidth = minWidth; + this.maxWidth = maxWidth; + this.minHeight = minHeight; + this.maxHeight = maxHeight; + } + + static AttachmentDimensions getAttachmentDimensions(Resources res) { + int defaultSize = + res.getDimensionPixelSize(R.dimen.message_bubble_image_default); + int minWidth = res.getDimensionPixelSize( + R.dimen.message_bubble_image_min_width); + int maxWidth = res.getDimensionPixelSize( + R.dimen.message_bubble_image_max_width); + int minHeight = res.getDimensionPixelSize( + R.dimen.message_bubble_image_min_height); + int maxHeight = res.getDimensionPixelSize( + R.dimen.message_bubble_image_max_height); + return new AttachmentDimensions(defaultSize, minWidth, maxWidth, + minHeight, minHeight); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java index 9c998e9eb..5f93fcd14 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationViewModel.java @@ -51,6 +51,7 @@ import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.now; +import static org.briarproject.briar.android.conversation.AttachmentDimensions.getAttachmentDimensions; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; import static org.briarproject.briar.android.util.UiUtils.observeForeverOnce; @@ -114,7 +115,7 @@ public class ConversationViewModel extends AndroidViewModel { this.settingsManager = settingsManager; this.privateMessageFactory = privateMessageFactory; this.attachmentController = new AttachmentController(messagingManager, - application.getResources()); + getAttachmentDimensions(application.getResources())); contactDeleted.setValue(false); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageHelper.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageHelper.java new file mode 100644 index 000000000..203a45222 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ImageHelper.java @@ -0,0 +1,29 @@ +package org.briarproject.briar.android.conversation; + +import android.support.annotation.Nullable; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import java.io.InputStream; + +@NotNullByDefault +interface ImageHelper { + + DecodeResult decodeStream(InputStream is); + + @Nullable + String getExtensionFromMimeType(String mimeType); + + class DecodeResult { + + final int width; + final int height; + final String mimeType; + + DecodeResult(int width, int height, String mimeType) { + this.width = width; + this.height = height; + this.mimeType = mimeType; + } + } +} diff --git a/briar-android/src/test/java/org/briarproject/briar/android/conversation/AttachmentControllerTest.java b/briar-android/src/test/java/org/briarproject/briar/android/conversation/AttachmentControllerTest.java new file mode 100644 index 000000000..d66b69697 --- /dev/null +++ b/briar-android/src/test/java/org/briarproject/briar/android/conversation/AttachmentControllerTest.java @@ -0,0 +1,153 @@ +package org.briarproject.briar.android.conversation; + +import org.briarproject.bramble.api.sync.MessageId; +import org.briarproject.bramble.test.BrambleMockTestCase; +import org.briarproject.briar.android.conversation.ImageHelper.DecodeResult; +import org.briarproject.briar.api.messaging.Attachment; +import org.briarproject.briar.api.messaging.AttachmentHeader; +import org.briarproject.briar.api.messaging.MessagingManager; +import org.jmock.Expectations; +import org.junit.Test; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import static org.briarproject.bramble.test.TestUtils.getRandomBytes; +import static org.briarproject.bramble.test.TestUtils.getRandomId; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class AttachmentControllerTest extends BrambleMockTestCase { + + private final AttachmentDimensions dimensions = new AttachmentDimensions( + 100, 50, 200, 75, 300 + ); + private final MessageId msgId = new MessageId(getRandomId()); + private final Attachment attachment = new Attachment( + new BufferedInputStream( + new ByteArrayInputStream(getRandomBytes(42)))); + + private final MessagingManager messagingManager = + context.mock(MessagingManager.class); + private final ImageHelper imageHelper = context.mock(ImageHelper.class); + private final AttachmentController controller = + new AttachmentController( + messagingManager, + dimensions, + imageHelper + ); + + @Test + public void testNoSize() { + String mimeType = "image/jpeg"; + AttachmentHeader h = getAttachmentHeader(mimeType); + + context.checking(new Expectations() {{ + oneOf(imageHelper).getExtensionFromMimeType(mimeType); + will(returnValue("jpg")); + }}); + + AttachmentItem item = + controller.getAttachmentItem(h, attachment, false); + assertEquals(mimeType, item.getMimeType()); + assertEquals("jpg", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testNoSizeWrongMimeTypeProducesError() { + String mimeType = "application/octet-stream"; + AttachmentHeader h = getAttachmentHeader(mimeType); + + context.checking(new Expectations() {{ + oneOf(imageHelper).getExtensionFromMimeType(mimeType); + will(returnValue(null)); + }}); + + AttachmentItem item = + controller.getAttachmentItem(h, attachment, false); + assertTrue(item.hasError()); + } + + @Test + public void testSmallJpegImage() { + String mimeType = "image/jpeg"; + AttachmentHeader h = getAttachmentHeader(mimeType); + + context.checking(new Expectations() {{ + oneOf(imageHelper).decodeStream(with(any(InputStream.class))); + will(returnValue(new DecodeResult(160, 240, mimeType))); + oneOf(imageHelper).getExtensionFromMimeType(mimeType); + will(returnValue("jpg")); + }}); + + AttachmentItem item = controller.getAttachmentItem(h, attachment, true); + assertEquals(msgId, item.getMessageId()); + assertEquals(160, item.getWidth()); + assertEquals(240, item.getHeight()); + assertEquals(160, item.getThumbnailWidth()); + assertEquals(240, item.getThumbnailHeight()); + assertEquals(mimeType, item.getMimeType()); + assertEquals("jpg", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testImageHealsWrongMimeType() { + AttachmentHeader h = getAttachmentHeader("image/png"); + + context.checking(new Expectations() {{ + oneOf(imageHelper).decodeStream(with(any(InputStream.class))); + will(returnValue(new DecodeResult(160, 240, "image/jpeg"))); + oneOf(imageHelper).getExtensionFromMimeType("image/jpeg"); + will(returnValue("jpg")); + }}); + + AttachmentItem item = controller.getAttachmentItem(h, attachment, true); + assertEquals("image/jpeg", item.getMimeType()); + assertEquals("jpg", item.getExtension()); + assertFalse(item.hasError()); + } + + @Test + public void testBigJpegImage() { + String mimeType = "image/jpeg"; + AttachmentHeader h = getAttachmentHeader(mimeType); + + context.checking(new Expectations() {{ + oneOf(imageHelper).decodeStream(with(any(InputStream.class))); + will(returnValue(new DecodeResult(1728, 2592, mimeType))); + oneOf(imageHelper).getExtensionFromMimeType(mimeType); + will(returnValue("jpg")); + }}); + + AttachmentItem item = controller.getAttachmentItem(h, attachment, true); + assertEquals(1728, item.getWidth()); + assertEquals(2592, item.getHeight()); + assertEquals(dimensions.maxWidth, item.getThumbnailWidth()); + assertEquals(dimensions.maxHeight, item.getThumbnailHeight()); + assertFalse(item.hasError()); + } + + @Test + public void testMalformedError() { + AttachmentHeader h = getAttachmentHeader("image/jpeg"); + + context.checking(new Expectations() {{ + oneOf(imageHelper).decodeStream(with(any(InputStream.class))); + will(returnValue(new DecodeResult(0, 0, ""))); + oneOf(imageHelper).getExtensionFromMimeType(""); + will(returnValue(null)); + }}); + + AttachmentItem item = controller.getAttachmentItem(h, attachment, true); + assertTrue(item.hasError()); + } + + private AttachmentHeader getAttachmentHeader(String contentType) { + return new AttachmentHeader(msgId, contentType); + } + +} diff --git a/briar-android/src/test/java/org/briarproject/briar/android/conversation/MarkEnforcingInputStreamTest.java b/briar-android/src/test/java/org/briarproject/briar/android/conversation/MarkEnforcingInputStreamTest.java new file mode 100644 index 000000000..45a30a499 --- /dev/null +++ b/briar-android/src/test/java/org/briarproject/briar/android/conversation/MarkEnforcingInputStreamTest.java @@ -0,0 +1,77 @@ +package org.briarproject.briar.android.conversation; + +import com.bumptech.glide.util.MarkEnforcingInputStream; + +import org.briarproject.bramble.test.BrambleTestCase; +import org.junit.Test; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class MarkEnforcingInputStreamTest extends BrambleTestCase { + + private final int readLimit = 4; + private final byte[] bytes = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6}; + + @Test + public void testPlainStreamReadsAllBytes() throws Exception { + InputStream is = getStream(); + is.mark(readLimit); + for (byte ignored : bytes) { + assertNotEquals(-1, is.read()); + } + assertEquals(0, is.available()); + is.close(); + } + + @Test + public void testMarkEnforcingStreamReadsUntilMarkLimit() throws Exception { + InputStream is = new MarkEnforcingInputStream(getStream()); + is.mark(readLimit); + assertEquals(readLimit, is.available()); + for (int i = 0; i < bytes.length; i++) { + if (i < readLimit) { + assertEquals(readLimit - i, is.available()); + assertNotEquals(-1, is.read()); + } else { + assertEquals(0, is.available()); + assertEquals(-1, is.read()); + } + } + assertEquals(0, is.available()); + is.close(); + } + + @Test + public void testMarkEnforcingStreamCanBeReset() throws Exception { + InputStream is = new MarkEnforcingInputStream(getStream()); + is.mark(readLimit); + assertEquals(readLimit, is.available()); + for (int i = 0; i < readLimit; i++) { + assertNotEquals(-1, is.read()); + } + assertEquals(0, is.available()); + is.reset(); + is.mark(readLimit); + assertEquals(readLimit, is.available()); + for (int i = 0; i < bytes.length; i++) { + if (i < readLimit) { + assertEquals(readLimit - i, is.available()); + assertNotEquals(-1, is.read()); + } else { + assertEquals(0, is.available()); + assertEquals(-1, is.read()); + } + } + is.close(); + } + + private InputStream getStream() { + return new BufferedInputStream(new ByteArrayInputStream(bytes)); + } + +}