Commit e964dae6 authored by akwizgran's avatar akwizgran

Merge branch '1468-image-size-tests' into 'master'

Add tests for parsing image sizes

See merge request !1026
parents ce1fde49 986d884b
Pipeline #2965 passed with stage
in 9 minutes and 17 seconds
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<MessageId, List<AttachmentItem>> 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<AttachmentItem> 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) {
......
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);
}
}
......@@ -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);
}
......
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;
}
}
}
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);
}
}
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));
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment