DO NOT MERGE: Tests for IMAP FETCH
Adding regression test for the new IMAP parser. Backport of Iac7f5c022e44ca5f06f735e145af15cc459eb61f Change-Id: Ic84172b6793a9837c2fc4a894fee141da3d19f1d
This commit is contained in:
parent
18d331898f
commit
a599ee773d
@ -23,9 +23,9 @@
|
||||
|
||||
-keep class * extends org.apache.james.mime4j.util.TempStorage
|
||||
|
||||
|
||||
# Keep names that are used only by unit tests
|
||||
|
||||
# Any methods whose name is '*ForTest' are preserved.
|
||||
-keep class ** {
|
||||
*** *ForTest(...);
|
||||
}
|
||||
@ -172,3 +172,7 @@
|
||||
-keep class org.apache.james.mime4j.message.Message {
|
||||
*;
|
||||
}
|
||||
|
||||
-keepclasseswithmembers class org.apache.commons.io.IOUtils {
|
||||
*** toByteArray(...);
|
||||
}
|
||||
|
@ -104,4 +104,8 @@ public class MimeMultipart extends Multipart {
|
||||
public InputStream getInputStream() throws MessagingException {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getSubTypeForTest() {
|
||||
return mSubType;
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,9 @@
|
||||
package com.android.email.mail.store;
|
||||
|
||||
import com.android.email.Email;
|
||||
import com.android.email.Utility;
|
||||
import com.android.email.mail.Address;
|
||||
import com.android.email.mail.Body;
|
||||
import com.android.email.mail.FetchProfile;
|
||||
import com.android.email.mail.Flag;
|
||||
import com.android.email.mail.Folder;
|
||||
@ -28,11 +30,15 @@ import com.android.email.mail.Transport;
|
||||
import com.android.email.mail.Folder.FolderType;
|
||||
import com.android.email.mail.Folder.OpenMode;
|
||||
import com.android.email.mail.Message.RecipientType;
|
||||
import com.android.email.mail.internet.MimeBodyPart;
|
||||
import com.android.email.mail.internet.MimeMultipart;
|
||||
import com.android.email.mail.internet.MimeUtility;
|
||||
import com.android.email.mail.internet.TextBody;
|
||||
import com.android.email.mail.store.ImapStore.ImapMessage;
|
||||
import com.android.email.mail.transport.MockTransport;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.MoreAsserts;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
@ -438,6 +444,224 @@ public class ImapStoreUnitTests extends AndroidTestCase {
|
||||
assertEquals("getUnreadMessageCount with literal string", 10, unreadCount);
|
||||
}
|
||||
|
||||
public void testFetchFlagEnvelope() throws MessagingException {
|
||||
final MockTransport mock = openAndInjectMockTransport();
|
||||
setupOpenFolder(mock);
|
||||
mFolder.open(OpenMode.READ_WRITE, null);
|
||||
final Message message = mFolder.createMessage("1");
|
||||
|
||||
final FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.FLAGS);
|
||||
fp.add(FetchProfile.Item.ENVELOPE);
|
||||
mock.expect(getNextTag(false) +
|
||||
" UID FETCH 1 \\(UID FLAGS INTERNALDATE RFC822\\.SIZE BODY\\.PEEK\\[HEADER.FIELDS" +
|
||||
" \\(date subject from content-type to cc message-id\\)\\]\\)",
|
||||
new String[] {
|
||||
"* 9 FETCH (UID 1 RFC822.SIZE 120626 INTERNALDATE \"17-May-2010 22:00:15 +0000\"" +
|
||||
"FLAGS (\\Seen) BODY[HEADER.FIELDS (date subject from content-type to cc" +
|
||||
" message-id)]" +
|
||||
" {279}",
|
||||
"From: Xxxxxx Yyyyy <userxx@android.com>",
|
||||
"Date: Mon, 17 May 2010 14:59:52 -0700",
|
||||
"Message-ID: <x0000000000000000000000000000000000000000000000y@android.com>",
|
||||
"Subject: ssubject",
|
||||
"To: android.test01@android.com",
|
||||
"Content-Type: multipart/mixed; boundary=a00000000000000000000000000b",
|
||||
"",
|
||||
")",
|
||||
getNextTag(true) + " OK SUCCESS"
|
||||
});
|
||||
mFolder.fetch(new Message[] { message }, fp, null);
|
||||
|
||||
assertEquals("android.test01@android.com", message.getHeader("to")[0]);
|
||||
assertEquals("Xxxxxx Yyyyy <userxx@android.com>", message.getHeader("from")[0]);
|
||||
assertEquals("multipart/mixed; boundary=a00000000000000000000000000b",
|
||||
message.getHeader("Content-Type")[0]);
|
||||
assertTrue(message.isSet(Flag.SEEN));
|
||||
}
|
||||
|
||||
public void testFetchBodyStructure() throws MessagingException {
|
||||
final MockTransport mock = openAndInjectMockTransport();
|
||||
setupOpenFolder(mock);
|
||||
mFolder.open(OpenMode.READ_WRITE, null);
|
||||
final Message message = mFolder.createMessage("1");
|
||||
|
||||
final FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.STRUCTURE);
|
||||
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODYSTRUCTURE\\)",
|
||||
new String[] {
|
||||
"* 9 FETCH (UID 1 BODYSTRUCTURE ((\"TEXT\" \"PLAIN\" (\"CHARSET\" \"ISO-8859-1\")" +
|
||||
" CID NIL \"7BIT\" 18 3 NIL NIL NIL)" +
|
||||
"(\"IMAGE\" \"PNG\"" +
|
||||
" (\"NAME\" \"device.png\") NIL NIL \"BASE64\" 117840 NIL (\"ATTACHMENT\"" +
|
||||
"(\"FILENAME\" \"device.png\")) NIL)" +
|
||||
"(\"TEXT\" \"HTML\"" +
|
||||
" () NIL NIL \"7BIT\" 100 NIL NIL (\"ATTACHMENT\"" +
|
||||
"(\"FILENAME\" \"attachment.html\" \"SIZE\" 555)) NIL)" +
|
||||
"\"MIXED\" (\"BOUNDARY\" \"00032556278a7005e40486d159ca\") NIL NIL))",
|
||||
getNextTag(true) + " OK SUCCESS"
|
||||
});
|
||||
mFolder.fetch(new Message[] { message }, fp, null);
|
||||
|
||||
// Check mime structure...
|
||||
Body body = message.getBody();
|
||||
assertTrue(body instanceof MimeMultipart);
|
||||
MimeMultipart mimeMultipart = (MimeMultipart) body;
|
||||
assertEquals(3, mimeMultipart.getCount());
|
||||
assertEquals("mixed", mimeMultipart.getSubTypeForTest());
|
||||
|
||||
Part part0 = mimeMultipart.getBodyPart(0);
|
||||
Part part1 = mimeMultipart.getBodyPart(1);
|
||||
Part part2 = mimeMultipart.getBodyPart(2);
|
||||
assertTrue(part0 instanceof MimeBodyPart);
|
||||
assertTrue(part1 instanceof MimeBodyPart);
|
||||
assertTrue(part2 instanceof MimeBodyPart);
|
||||
|
||||
MimeBodyPart mimePart0 = (MimeBodyPart) part0; // text/plain
|
||||
MimeBodyPart mimePart1 = (MimeBodyPart) part1; // image/png
|
||||
MimeBodyPart mimePart2 = (MimeBodyPart) part2; // text/html
|
||||
|
||||
MoreAsserts.assertEquals(
|
||||
new String[] {"text/plain;\n CHARSET=\"ISO-8859-1\""},
|
||||
part0.getHeader("Content-Type")
|
||||
);
|
||||
MoreAsserts.assertEquals(
|
||||
new String[] {"image/png;\n NAME=\"device.png\""},
|
||||
part1.getHeader("Content-Type")
|
||||
);
|
||||
MoreAsserts.assertEquals(
|
||||
new String[] {"text/html"},
|
||||
part2.getHeader("Content-Type")
|
||||
);
|
||||
|
||||
MoreAsserts.assertEquals(
|
||||
new String[] {"CID"},
|
||||
part0.getHeader("Content-ID")
|
||||
);
|
||||
assertNull(
|
||||
part1.getHeader("Content-ID")
|
||||
);
|
||||
assertNull(
|
||||
part2.getHeader("Content-ID")
|
||||
);
|
||||
|
||||
MoreAsserts.assertEquals(
|
||||
new String[] {"7BIT"},
|
||||
part0.getHeader("Content-Transfer-Encoding")
|
||||
);
|
||||
MoreAsserts.assertEquals(
|
||||
new String[] {"BASE64"},
|
||||
part1.getHeader("Content-Transfer-Encoding")
|
||||
);
|
||||
MoreAsserts.assertEquals(
|
||||
new String[] {"7BIT"},
|
||||
part2.getHeader("Content-Transfer-Encoding")
|
||||
);
|
||||
|
||||
MoreAsserts.assertEquals(
|
||||
new String[] {";\n size=18"}, // TODO Is that right?
|
||||
part0.getHeader("Content-Disposition")
|
||||
);
|
||||
MoreAsserts.assertEquals(
|
||||
new String[] {"attachment;\n filename=\"device.png\";\n size=117840"},
|
||||
part1.getHeader("Content-Disposition")
|
||||
);
|
||||
MoreAsserts.assertEquals(
|
||||
new String[] {"attachment;\n filename=\"attachment.html\";\n size=\"555\""},
|
||||
part2.getHeader("Content-Disposition")
|
||||
);
|
||||
|
||||
// TODO Test for quote: If a filename contains ", it should become %22,
|
||||
// which isn't implemented.
|
||||
}
|
||||
|
||||
public void testFetchBodySane() throws MessagingException {
|
||||
final MockTransport mock = openAndInjectMockTransport();
|
||||
setupOpenFolder(mock);
|
||||
mFolder.open(OpenMode.READ_WRITE, null);
|
||||
final Message message = mFolder.createMessage("1");
|
||||
|
||||
final FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.BODY_SANE);
|
||||
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[\\]<0.51200>\\)",
|
||||
new String[] {
|
||||
"* 9 FETCH (UID 1 BODY[] {23}",
|
||||
"from: a@b.com", // 15 bytes
|
||||
"", // 2
|
||||
"test", // 6
|
||||
")",
|
||||
getNextTag(true) + " OK SUCCESS"
|
||||
});
|
||||
mFolder.fetch(new Message[] { message }, fp, null);
|
||||
assertEquals("a@b.com", message.getHeader("from")[0]);
|
||||
}
|
||||
|
||||
public void testFetchBody() throws MessagingException {
|
||||
final MockTransport mock = openAndInjectMockTransport();
|
||||
setupOpenFolder(mock);
|
||||
mFolder.open(OpenMode.READ_WRITE, null);
|
||||
final Message message = mFolder.createMessage("1");
|
||||
|
||||
final FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.BODY);
|
||||
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[\\]\\)",
|
||||
new String[] {
|
||||
"* 9 FETCH (UID 1 BODY[] {23}",
|
||||
"from: a@b.com", // 15 bytes
|
||||
"", // 2
|
||||
"test", // 6
|
||||
")",
|
||||
getNextTag(true) + " OK SUCCESS"
|
||||
});
|
||||
mFolder.fetch(new Message[] { message }, fp, null);
|
||||
assertEquals("a@b.com", message.getHeader("from")[0]);
|
||||
}
|
||||
|
||||
public void testFetchAttachment() throws Exception {
|
||||
MockTransport mock = openAndInjectMockTransport();
|
||||
setupOpenFolder(mock);
|
||||
mFolder.open(OpenMode.READ_WRITE, null);
|
||||
final Message message = mFolder.createMessage("1");
|
||||
|
||||
final FetchProfile fp = new FetchProfile();
|
||||
fp.add(FetchProfile.Item.STRUCTURE);
|
||||
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODYSTRUCTURE\\)",
|
||||
new String[] {
|
||||
"* 9 FETCH (UID 1 BODYSTRUCTURE ((\"TEXT\" \"PLAIN\" (\"CHARSET\" \"ISO-8859-1\")" +
|
||||
" CID NIL \"7BIT\" 18 3 NIL NIL NIL)" +
|
||||
"(\"IMAGE\" \"PNG\"" +
|
||||
" (\"NAME\" \"device.png\") NIL NIL \"BASE64\" 117840 NIL (\"ATTACHMENT\"" +
|
||||
"(\"FILENAME\" \"device.png\")) NIL)" +
|
||||
"\"MIXED\"))",
|
||||
getNextTag(true) + " OK SUCCESS"
|
||||
});
|
||||
mFolder.fetch(new Message[] { message }, fp, null);
|
||||
|
||||
// Check mime structure, and get the second part.
|
||||
Body body = message.getBody();
|
||||
assertTrue(body instanceof MimeMultipart);
|
||||
MimeMultipart mimeMultipart = (MimeMultipart) body;
|
||||
assertEquals(2, mimeMultipart.getCount());
|
||||
|
||||
Part part1 = mimeMultipart.getBodyPart(1);
|
||||
assertTrue(part1 instanceof MimeBodyPart);
|
||||
MimeBodyPart mimePart1 = (MimeBodyPart) part1;
|
||||
|
||||
// Fetch the second part
|
||||
fp.clear();
|
||||
fp.add(mimePart1);
|
||||
mock.expect(getNextTag(false) + " UID FETCH 1 \\(UID BODY.PEEK\\[2\\]\\)",
|
||||
new String[] {
|
||||
"* 9 FETCH (UID 1 BODY[2] {4}",
|
||||
"YWJj)", // abc in base64
|
||||
getNextTag(true) + " OK SUCCESS"
|
||||
});
|
||||
mFolder.fetch(new Message[] { message }, fp, null);
|
||||
|
||||
assertEquals("abc",
|
||||
Utility.fromUtf8(IOUtils.toByteArray(mimePart1.getBody().getInputStream())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for proper operations on servers that return "NIL" for empty message bodies.
|
||||
*/
|
||||
|
@ -39,12 +39,12 @@ public class MockTransport implements Transport {
|
||||
// All flags defining debug or development code settings must be FALSE
|
||||
// when code is checked in or released.
|
||||
private static boolean DEBUG_LOG_STREAMS = true;
|
||||
|
||||
|
||||
private static String LOG_TAG = "MockTransport";
|
||||
|
||||
private boolean mSslAllowed = false;
|
||||
private boolean mTlsAllowed = false;
|
||||
|
||||
|
||||
private boolean mOpen;
|
||||
private boolean mInputOpen;
|
||||
private int mConnectionSecurity;
|
||||
@ -58,23 +58,23 @@ public class MockTransport implements Transport {
|
||||
public static final int ACTION_INJECT_TEXT = 0;
|
||||
public static final int ACTION_SERVER_CLOSE = 1;
|
||||
public static final int ACTION_CLIENT_CLOSE = 2;
|
||||
|
||||
|
||||
int mAction;
|
||||
String mPattern;
|
||||
String[] mResponses;
|
||||
|
||||
|
||||
Transaction(String pattern, String[] responses) {
|
||||
mAction = ACTION_INJECT_TEXT;
|
||||
mPattern = pattern;
|
||||
mResponses = responses;
|
||||
}
|
||||
|
||||
|
||||
Transaction(int otherType) {
|
||||
mAction = otherType;
|
||||
mPattern = null;
|
||||
mResponses = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
switch (mAction) {
|
||||
@ -89,7 +89,7 @@ public class MockTransport implements Transport {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private ArrayList<Transaction> mPairs = new ArrayList<Transaction>();
|
||||
|
||||
/**
|
||||
@ -108,7 +108,7 @@ public class MockTransport implements Transport {
|
||||
public void expect(String pattern, String response) {
|
||||
expect(pattern, (response == null) ? null : new String[] { response });
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Give the mock a pattern to wait for and a multi-line response to send back.
|
||||
* @param pattern Java RegEx to wait for
|
||||
@ -127,7 +127,7 @@ public class MockTransport implements Transport {
|
||||
expect("^" + Pattern.quote(literal) + "$", responses);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Tell the Mock Transport that we expect it to be closed. This will preserve
|
||||
* the remaining entries in the expect() stream and allow us to "ride over" the close (which
|
||||
* would normally reset everything).
|
||||
@ -135,7 +135,7 @@ public class MockTransport implements Transport {
|
||||
public void expectClose() {
|
||||
mPairs.add(new Transaction(Transaction.ACTION_CLIENT_CLOSE));
|
||||
}
|
||||
|
||||
|
||||
private void sendResponse(String[] responses) {
|
||||
for (String s : responses) {
|
||||
mQueuedInput.add(s);
|
||||
@ -145,7 +145,7 @@ public class MockTransport implements Transport {
|
||||
public boolean canTrySslSecurity() {
|
||||
return (mConnectionSecurity == CONNECTION_SECURITY_SSL);
|
||||
}
|
||||
|
||||
|
||||
public boolean canTryTlsSecurity() {
|
||||
return (mConnectionSecurity == Transport.CONNECTION_SECURITY_TLS);
|
||||
}
|
||||
@ -155,7 +155,7 @@ public class MockTransport implements Transport {
|
||||
}
|
||||
|
||||
/**
|
||||
* This simulates a condition where the server has closed its side, causing
|
||||
* This simulates a condition where the server has closed its side, causing
|
||||
* reads to fail.
|
||||
*/
|
||||
public void closeInputStream() {
|
||||
@ -199,7 +199,7 @@ public class MockTransport implements Transport {
|
||||
|
||||
/**
|
||||
* This normally serves as a pseudo-clone, for use by Imap. For the purposes of unit testing,
|
||||
* until we need something more complex, we'll just return the actual MockTransport. Then we
|
||||
* until we need something more complex, we'll just return the actual MockTransport. Then we
|
||||
* don't have to worry about dealing with test metadata like the expects list or socket state.
|
||||
*/
|
||||
public Transport newInstanceWithConfiguration() {
|
||||
@ -239,9 +239,9 @@ public class MockTransport implements Transport {
|
||||
* from the mQueuedInput list, but if the list is empty, we also peek the expect list. This
|
||||
* supports banners, multi-line responses, and any other cases where we respond without
|
||||
* a specific expect pattern.
|
||||
*
|
||||
*
|
||||
* If no response text is available, we assert (failing our test) as an underflow.
|
||||
*
|
||||
*
|
||||
* Logs the read text if DEBUG_LOG_STREAMS is true.
|
||||
*/
|
||||
public String readLine() throws IOException {
|
||||
@ -251,7 +251,7 @@ public class MockTransport implements Transport {
|
||||
}
|
||||
// if there's nothing to read, see if we can find a null-pattern response
|
||||
if (0 == mQueuedInput.size()) {
|
||||
Transaction pair = mPairs.get(0);
|
||||
Transaction pair = mPairs.size() > 0 ? mPairs.get(0) : null;
|
||||
if (pair != null && pair.mPattern == null) {
|
||||
mPairs.remove(0);
|
||||
sendResponse(pair.mResponses);
|
||||
@ -278,7 +278,7 @@ public class MockTransport implements Transport {
|
||||
|
||||
public void setSoTimeout(int timeoutMilliseconds) /* throws SocketException */ {
|
||||
}
|
||||
|
||||
|
||||
public void setUri(URI uri, int defaultPort) {
|
||||
SmtpSenderUnitTests.assertTrue("Don't call setUri on a mock transport", false);
|
||||
}
|
||||
@ -289,7 +289,7 @@ public class MockTransport implements Transport {
|
||||
* If the string was expected, we push the corresponding responses into the mQueuedInput
|
||||
* list, for subsequent calls to readLine(). If the string does not match, we assert
|
||||
* the mismatch. If no string was expected, we assert it as an overflow.
|
||||
*
|
||||
*
|
||||
* Logs the written text if DEBUG_LOG_STREAMS is true.
|
||||
*/
|
||||
public void writeLine(String s, String sensitiveReplacement) /* throws IOException */ {
|
||||
@ -307,7 +307,7 @@ public class MockTransport implements Transport {
|
||||
sendResponse(pair.mResponses);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is an InputStream that satisfies the needs of getInputStream()
|
||||
*/
|
||||
@ -315,7 +315,7 @@ public class MockTransport implements Transport {
|
||||
|
||||
byte[] mNextLine = null;
|
||||
int mNextIndex = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Reads from the same input buffer as readLine()
|
||||
*/
|
||||
@ -324,11 +324,11 @@ public class MockTransport implements Transport {
|
||||
if (!mInputOpen) {
|
||||
throw new IOException();
|
||||
}
|
||||
|
||||
|
||||
if (mNextLine != null && mNextIndex < mNextLine.length) {
|
||||
return mNextLine[mNextIndex++];
|
||||
}
|
||||
|
||||
|
||||
// previous line was exhausted so try to get another one
|
||||
String next = readLine();
|
||||
if (next == null) {
|
||||
@ -340,12 +340,12 @@ public class MockTransport implements Transport {
|
||||
if (mNextLine != null && mNextIndex < mNextLine.length) {
|
||||
return mNextLine[mNextIndex++];
|
||||
}
|
||||
|
||||
// no joy - throw an exception
|
||||
throw new IOException();
|
||||
|
||||
// no joy - throw an exception
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is an OutputStream that satisfies the needs of getOutputStream()
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user