From a50fc99b0c433f0cde31ba1c7ab87fb9ea86345d Mon Sep 17 00:00:00 2001 From: Todd Kennedy Date: Tue, 19 Apr 2011 11:32:06 -0700 Subject: [PATCH] Use Account instead of URI to create transports There's no need to create a URI just to rip in appart again. Additionally, to support additional changes (i.e. to use Mailbox instead of Folder in the MessageController), we need to store the actual Account. NOTE -- This change only affects IMAP and POP3. SMTP will come in a follow-on CL Change-Id: I400036a17271c99272fd9c603547dcd713b50b9d --- .../emailcommon/provider/EmailContent.java | 13 + proguard.flags | 7 +- src/com/android/email/Controller.java | 13 +- .../android/email/MessagingController.java | 20 +- .../setup/AccountCheckSettingsFragment.java | 8 +- .../activity/setup/AccountSettingsXL.java | 2 +- src/com/android/email/mail/Store.java | 98 ++++-- src/com/android/email/mail/Transport.java | 66 ++-- .../android/email/mail/store/ImapStore.java | 67 ++-- .../android/email/mail/store/Pop3Store.java | 93 +++--- .../email/mail/transport/MailTransport.java | 15 +- .../email/mail/transport/SmtpSender.java | 2 + .../com/android/email/mail/StoreTests.java | 166 +++++----- .../email/mail/store/ImapStoreUnitTests.java | 71 ++-- .../email/mail/store/Pop3StoreUnitTests.java | 304 +++++++++--------- .../email/mail/transport/MockTransport.java | 16 +- 16 files changed, 539 insertions(+), 422 deletions(-) diff --git a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java index 4f88cfb13..e0503adbc 100644 --- a/emailcommon/src/com/android/emailcommon/provider/EmailContent.java +++ b/emailcommon/src/com/android/emailcommon/provider/EmailContent.java @@ -2788,6 +2788,19 @@ public abstract class EmailContent { } } + /** + * Returns the login information. [0] is the username and [1] is the password. If + * {@link #FLAG_AUTHENTICATE} is not set, {@code null} is returned. + */ + public String[] getLogin() { + if ((mFlags & FLAG_AUTHENTICATE) != 0) { + String trimUser = (mLogin != null) ? mLogin.trim() : ""; + String password = (mPassword != null) ? mPassword : ""; + return new String[] { trimUser, password }; + } + return null; + } + /** * Sets the connection values of the auth structure per the given scheme, host and port. */ diff --git a/proguard.flags b/proguard.flags index d1ebcb8f8..7ed8d21de 100644 --- a/proguard.flags +++ b/proguard.flags @@ -13,10 +13,15 @@ public *** newInstance(android.content.Context, java.lang.String); } +# TODO remove after converting Sender#instantiateSender() to use Account instead of URI -keepclasseswithmembers class * { public *** newInstance(java.lang.String, android.content.Context, com.android.email.mail.Store$PersistentDataCallbacks); } +-keepclasseswithmembers class * { + public *** newInstance(com.android.emailcommon.provider.EmailContent$Account, android.content.Context, com.android.email.mail.Store$PersistentDataCallbacks); +} + -keepclasseswithmembers class android.content.SharedPreferences$Editor { *** apply(); } @@ -144,7 +149,7 @@ *** setTransport(com.android.email.mail.Transport); } --keepclasseswithmembers class com.android.email.mail.store.ImapStore$ImapFolder { +-keepclasseswithmembers class com.android.email.mail.store.ImapFolder { *** getMessages(int, int, com.android.emailcommon.mail.Folder$MessageRetrievalListener); *** getMessages(com.android.emailcommon.mail.Folder$MessageRetrievalListener); *** getMessages(java.lang.String[], com.android.emailcommon.mail.Folder$MessageRetrievalListener); diff --git a/src/com/android/email/Controller.java b/src/com/android/email/Controller.java index 7dff4f464..af18efe40 100644 --- a/src/com/android/email/Controller.java +++ b/src/com/android/email/Controller.java @@ -1006,7 +1006,7 @@ public class Controller { } /** - * Delete an account synchronously. Intended to be used only by unit tests. + * Delete an account synchronously. */ public void deleteAccountSync(long accountId, Context context) { try { @@ -1017,12 +1017,13 @@ public class Controller { return; // Already deleted? } - final String accountUri = account.getStoreUri(context); - // Delete Remote store at first. - if (!TextUtils.isEmpty(accountUri)) { - Store.getInstance(accountUri, context, null).delete(); + try { + // Delete Remote store at first. + Store.getInstance(account, context, null).delete(); // Remove the Store instance from cache. - Store.removeInstance(accountUri); + Store.removeInstance(account, context); + } catch (MessagingException e) { + Log.w(Logging.LOG_TAG, "Failed to delete store", e); } Uri uri = ContentUris.withAppendedId( diff --git a/src/com/android/email/MessagingController.java b/src/com/android/email/MessagingController.java index 01a982dcd..8976b177c 100644 --- a/src/com/android/email/MessagingController.java +++ b/src/com/android/email/MessagingController.java @@ -253,7 +253,7 @@ public class MessagingController implements Runnable { // Step 1: Get remote folders, make a list, and add any local folders // that don't already exist. - Store store = Store.getInstance(account.getStoreUri(mContext), mContext, null); + Store store = Store.getInstance(account, mContext, null); Folder[] remoteFolders = store.getAllFolders(); @@ -383,7 +383,7 @@ public class MessagingController implements Runnable { StoreSynchronizer.SyncResults results; // Select generic sync or store-specific sync - Store remoteStore = Store.getInstance(account.getStoreUri(mContext), mContext, null); + Store remoteStore = Store.getInstance(account, mContext, null); StoreSynchronizer customSync = remoteStore.getMessageSynchronizer(); if (customSync == null) { results = synchronizeMailboxGeneric(account, folder); @@ -512,7 +512,7 @@ public class MessagingController implements Runnable { // 2. Open the remote folder and create the remote folder if necessary - Store remoteStore = Store.getInstance(account.getStoreUri(mContext), mContext, null); + Store remoteStore = Store.getInstance(account, mContext, null); Folder remoteFolder = remoteStore.getFolder(folder.mDisplayName); /* @@ -1130,7 +1130,7 @@ public class MessagingController implements Runnable { // Load the remote store if it will be needed if (remoteStore == null && deleteFromTrash) { - remoteStore = Store.getInstance(account.getStoreUri(mContext), mContext, null); + remoteStore = Store.getInstance(account, mContext, null); } // Dispatch here for specific change types @@ -1202,7 +1202,7 @@ public class MessagingController implements Runnable { // Load the remote store if it will be needed if (remoteStore == null) { remoteStore = - Store.getInstance(account.getStoreUri(mContext), mContext, null); + Store.getInstance(account, mContext, null); } // Load the mailbox if it will be needed if (mailbox == null) { @@ -1232,7 +1232,7 @@ public class MessagingController implements Runnable { // Load the remote store if it will be needed if (remoteStore == null) { remoteStore = - Store.getInstance(account.getStoreUri(mContext), mContext, null); + Store.getInstance(account, mContext, null); } // Load the mailbox if it will be needed if (mailbox == null) { @@ -1319,7 +1319,7 @@ public class MessagingController implements Runnable { // Load the remote store if it will be needed if (remoteStore == null && (changeMoveToTrash || changeRead || changeFlagged || changeMailbox)) { - remoteStore = Store.getInstance(account.getStoreUri(mContext), mContext, null); + remoteStore = Store.getInstance(account, mContext, null); } // Dispatch here for specific change types @@ -1829,7 +1829,7 @@ public class MessagingController implements Runnable { } Store remoteStore = - Store.getInstance(account.getStoreUri(mContext), mContext, null); + Store.getInstance(account, mContext, null); Folder remoteFolder = remoteStore.getFolder(mailbox.mDisplayName); remoteFolder.open(OpenMode.READ_WRITE, null); @@ -1922,7 +1922,7 @@ public class MessagingController implements Runnable { } Store remoteStore = - Store.getInstance(account.getStoreUri(mContext), mContext, null); + Store.getInstance(account, mContext, null); Folder remoteFolder = remoteStore.getFolder(mailbox.mDisplayName); remoteFolder.open(OpenMode.READ_WRITE, null); @@ -2020,7 +2020,7 @@ public class MessagingController implements Runnable { mListeners.sendPendingMessagesStarted(account.mId, -1); Sender sender = Sender.getInstance(mContext, account.getSenderUri(mContext)); - Store remoteStore = Store.getInstance(account.getStoreUri(mContext), mContext, null); + Store remoteStore = Store.getInstance(account, mContext, null); boolean requireMoveMessageToSentFolder = remoteStore.requireCopyMessageToSentFolder(); ContentValues moveToSentValues = null; if (requireMoveMessageToSentFolder) { diff --git a/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java b/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java index 2b16a6145..2313d75d2 100644 --- a/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java +++ b/src/com/android/email/activity/setup/AccountCheckSettingsFragment.java @@ -390,7 +390,7 @@ public class AccountCheckSettingsFragment extends Fragment { final Context mContext; final int mMode; - final String mStoreUri; + final Account mAccount; final String mStoreHost; final String mSenderUri; final String mCheckEmail; @@ -404,7 +404,7 @@ public class AccountCheckSettingsFragment extends Fragment { public AccountCheckTask(int mode, Account checkAccount) { mContext = getActivity().getApplicationContext(); mMode = mode; - mStoreUri = checkAccount.getStoreUri(mContext); + mAccount = checkAccount; mStoreHost = checkAccount.mHostAuthRecv.mAddress; mSenderUri = checkAccount.getSenderUri(mContext); mCheckEmail = checkAccount.mEmailAddress; @@ -422,7 +422,7 @@ public class AccountCheckSettingsFragment extends Fragment { if (isCancelled()) return null; publishProgress(STATE_CHECK_AUTODISCOVER); Log.d(Logging.LOG_TAG, "Begin auto-discover for " + mCheckEmail); - Store store = Store.getInstance(mStoreUri, mContext, null); + Store store = Store.getInstance(mAccount, mContext, null); Bundle result = store.autoDiscover(mContext, mCheckEmail, mCheckPassword); // Result will be one of: // null: remote exception - proceed to manual setup @@ -450,7 +450,7 @@ public class AccountCheckSettingsFragment extends Fragment { if (isCancelled()) return null; Log.d(Logging.LOG_TAG, "Begin check of incoming email settings"); publishProgress(STATE_CHECK_INCOMING); - Store store = Store.getInstance(mStoreUri, mContext, null); + Store store = Store.getInstance(mAccount, mContext, null); Bundle bundle = store.checkSettings(); int resultCode = MessagingException.UNSPECIFIED_EXCEPTION; if (bundle != null) { diff --git a/src/com/android/email/activity/setup/AccountSettingsXL.java b/src/com/android/email/activity/setup/AccountSettingsXL.java index 22b015282..9f2df3189 100644 --- a/src/com/android/email/activity/setup/AccountSettingsXL.java +++ b/src/com/android/email/activity/setup/AccountSettingsXL.java @@ -613,7 +613,7 @@ public class AccountSettingsXL extends PreferenceActivity { */ public void onIncomingSettings(Account account) { try { - Store store = Store.getInstance(account.getStoreUri(this), getApplication(), null); + Store store = Store.getInstance(account, getApplication(), null); if (store != null) { Class setting = store.getSettingActivityClass(); if (setting != null) { diff --git a/src/com/android/email/mail/Store.java b/src/com/android/email/mail/Store.java index fe48fc386..9e2829a69 100644 --- a/src/com/android/email/mail/Store.java +++ b/src/com/android/email/mail/Store.java @@ -21,12 +21,17 @@ import com.android.email.R; import com.android.emailcommon.Logging; import com.android.emailcommon.mail.Folder; import com.android.emailcommon.mail.MessagingException; +import com.android.emailcommon.provider.EmailContent.Account; +import com.android.emailcommon.provider.EmailContent.HostAuth; +import com.android.emailcommon.utility.Utility; +import com.google.common.annotations.VisibleForTesting; import org.xmlpull.v1.XmlPullParserException; import android.content.Context; import android.content.res.XmlResourceParser; import android.os.Bundle; +import android.text.TextUtils; import android.util.Log; import java.io.IOException; @@ -59,18 +64,20 @@ public abstract class Store { * should be returned on FetchProfile.Item.BODY_SANE requests. */ public static final int FETCH_BODY_SANE_SUGGESTED_SIZE = (50 * 1024); - private static final HashMap sStores = new HashMap(); + @VisibleForTesting + static final HashMap sStores = new HashMap(); /** * Static named constructor. It should be overrode by extending class. * Because this method will be called through reflection, it can not be protected. */ - public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks) - throws MessagingException { - throw new MessagingException("Store.newInstance: Unknown scheme in " + uri); + public static Store newInstance(Account account, Context context, + PersistentDataCallbacks callbacks) throws MessagingException { + throw new MessagingException("Store#newInstance: Unknown scheme in " + + account.mDisplayName); } - private static Store instantiateStore(String className, String uri, Context context, + private static Store instantiateStore(String className, Account account, Context context, PersistentDataCallbacks callbacks) throws MessagingException { Object o = null; @@ -78,18 +85,19 @@ public abstract class Store { Class c = Class.forName(className); // and invoke "newInstance" class method and instantiate store object. java.lang.reflect.Method m = - c.getMethod("newInstance", String.class, Context.class, + c.getMethod("newInstance", Account.class, Context.class, PersistentDataCallbacks.class); - o = m.invoke(null, uri, context, callbacks); + // TODO Do the stores _really need a context? Is there a way to not pass it along? + o = m.invoke(null, account, context, callbacks); } catch (Exception e) { Log.d(Logging.LOG_TAG, String.format( - "exception %s invoking %s.newInstance.(String, Context) method for %s", - e.toString(), className, uri)); - throw new MessagingException("can not instantiate Store object for " + uri); + "exception %s invoking method %s#newInstance(Account, Context) for %s", + e.toString(), className, account.mDisplayName)); + throw new MessagingException("can not instantiate Store for " + account.mDisplayName); } if (!(o instanceof Store)) { throw new MessagingException( - uri + ": " + className + " create incompatible object"); + account.mDisplayName + ": " + className + " create incompatible object"); } return (Store) o; } @@ -149,38 +157,54 @@ public abstract class Store { } /** - * Get an instance of a mail store. The URI is parsed as a standard URI and - * the scheme is used to determine which protocol will be used. - * - * Although the URI format is somewhat protocol-specific, we use the following - * guidelines wherever possible: - * - * scheme [+ security [+]] :// username : password @ host [ / resource ] - * - * Typical schemes include imap, pop3, local, eas. - * Typical security models include SSL or TLS. - * A + after the security identifier indicates "required". + * Gets a unique key for the given account. + * @throws MessagingException If the account is not setup properly (i.e. there is no address + * or login) + */ + @VisibleForTesting + static String getStoreKey(Context context, Account account) throws MessagingException { + final StringBuffer key = new StringBuffer(); + final HostAuth recvAuth = account.getOrCreateHostAuthRecv(context); + if (recvAuth.mAddress == null) { + throw new MessagingException("Cannot find store for account " + account.mDisplayName); + } + final String address = recvAuth.mAddress.trim(); + if (TextUtils.isEmpty(address)) { + throw new MessagingException("Cannot find store for account " + account.mDisplayName); + } + key.append(address); + if (recvAuth.mLogin != null) { + key.append(recvAuth.mLogin.trim()); + } + return key.toString(); + } + + /** + * Get an instance of a mail store for the given account. The account must be valid (i.e. has + * at least an incoming server name). * * Username, password, and host are as expected. * Resource is protocol specific. For example, IMAP uses it as the path prefix. EAS uses it * as the domain. * - * @param uri The URI of the store. + * @param account The account of the store. * @return an initialized store of the appropriate class - * @throws MessagingException + * @throws MessagingException If the store cannot be obtained or if the account is invalid. */ - public synchronized static Store getInstance(String uri, Context context, + public synchronized static Store getInstance(Account account, Context context, PersistentDataCallbacks callbacks) throws MessagingException { - Store store = sStores.get(uri); + String storeKey = getStoreKey(context, account); + Store store = sStores.get(storeKey); if (store == null) { - context = context.getApplicationContext(); - StoreInfo info = StoreInfo.getStoreInfo(uri, context); + Context appContext = context.getApplicationContext(); + HostAuth recvAuth = account.getOrCreateHostAuthRecv(context); + StoreInfo info = StoreInfo.getStoreInfo(recvAuth.mProtocol, context); if (info != null) { - store = instantiateStore(info.mClassName, uri, context, callbacks); + store = instantiateStore(info.mClassName, account, appContext, callbacks); } if (store != null) { - sStores.put(uri, store); + sStores.put(storeKey, store); } } else { // update the callbacks, which may have been null at creation time. @@ -188,21 +212,25 @@ public abstract class Store { } if (store == null) { - throw new MessagingException("Unable to locate an applicable Store for " + uri); + throw new MessagingException("Cannot find store for account " + account.mDisplayName); } return store; } /** - * Delete an instance of a mail store. + * Delete the mail store associated with the given account. The account must be valid (i.e. has + * at least an incoming server name). * * The store should have been notified already by calling delete(), and the caller should * also take responsibility for deleting the matching LocalStore, etc. - * @param storeUri the store to be removed + * + * @throws MessagingException If the store cannot be removed or if the account is invalid. */ - public synchronized static void removeInstance(String storeUri) { - sStores.remove(storeUri); + public synchronized static void removeInstance(Account account, Context context) + throws MessagingException { + final String storeKey = getStoreKey(context, account); + sStores.remove(storeKey); } /** diff --git a/src/com/android/email/mail/Transport.java b/src/com/android/email/mail/Transport.java index 45ed3f4e7..3de50f5ac 100644 --- a/src/com/android/email/mail/Transport.java +++ b/src/com/android/email/mail/Transport.java @@ -27,27 +27,27 @@ import java.net.SocketException; import java.net.URI; /** - * This interface defines a "transport", which is defined here as being one layer below the - * specific wire protocols such as POP3, IMAP, or SMTP. - * + * This interface defines a "transport", which is defined here as being one layer below the + * specific wire protocols such as POP3, IMAP, or SMTP. + * * Practically speaking, it provides a definition of the common functionality between them * (dealing with sockets & streams, SSL, logging, and so forth), and provides a seam just below * the individual protocols to enable better testing. - * + * * The following features are supported and presumed to be common: - * + * * Interpretation of URI * Support for SSL and TLS wireline security */ public interface Transport { - + /** * Connection security options for transport that supports SSL and/or TLS */ public static final int CONNECTION_SECURITY_NONE = 0; public static final int CONNECTION_SECURITY_SSL = 1; public static final int CONNECTION_SECURITY_TLS = 2; - + /** * Get a new transport, using an existing one as a model. The new transport is configured as if * setUri() and setSecurity() have been called, but not opened or connected in any way. @@ -57,27 +57,41 @@ public interface Transport { /** * Set the Uri for the connection. - * + * * @param uri The Uri for the connection * @param defaultPort If the Uri does not include an explicit port, this value will be used. + * @deprecated use the individual methods {@link #setHost(String)} and {@link #setPort(int)} */ + @Deprecated public void setUri(URI uri, int defaultPort); - + /** - * @return Returns the host part of the Uri + * Sets the host + */ + public void setHost(String host); + + /** + * Sets the port + */ + public void setPort(int port); + + /** + * Returns the host or {@code null} if none was specified. */ public String getHost(); - + /** - * @return Returns the port (either from the Uri or from the default) + * Returns the port or {@code 0} if none was specified. */ public int getPort(); - + /** - * Returns the user info parts of the Uri, if any were supplied. Typically, [0] is the user + * Returns the user info parts of the Uri, if any were supplied. Typically, [0] is the user * and [1] is the password. * @return Returns the user info parts of the Uri. Null if none were supplied. + * @deprecated do not use this method. user info parts should not be stored in the transport. */ + @Deprecated public String[] getUserInfoParts(); /** @@ -86,17 +100,17 @@ public interface Transport { * @param trustAllCertificates true to allow unverifiable certificates to be used */ public void setSecurity(int connectionSecurity, boolean trustAllCertificates); - + /** * @return Returns the desired security mode for this connection. */ public int getSecurity(); - + /** * @return true if the security mode indicates that SSL is possible */ public boolean canTrySslSecurity(); - + /** * @return true if the security mode indicates that TLS is possible */ @@ -118,35 +132,35 @@ public interface Transport { * Attempts to open the connection using the supplied parameters, and using SSL if indicated. */ public void open() throws MessagingException, CertificateValidationException; - + /** * Attempts to reopen the connection using TLS. */ public void reopenTls() throws MessagingException; - + /** * @return true if the connection is open */ public boolean isOpen(); - + /** * Closes the connection. Does not send any closure messages, simply closes the socket and the - * associated streams. Best effort only. Catches all exceptions and always returns. - * + * associated streams. Best effort only. Catches all exceptions and always returns. + * * MUST NOT throw any exceptions. */ public void close(); - + /** * @return returns the active input stream */ public InputStream getInputStream(); - + /** * @return returns the active output stream */ public OutputStream getOutputStream(); - + /** * Write a single line to the server, and may generate a log entry (if enabled). * @param s The text to send to the server. @@ -154,7 +168,7 @@ public interface Transport { * please pass a replacement string here (for logging). Most callers simply pass null, */ void writeLine(String s, String sensitiveReplacement) throws IOException; - + /** * Reads a single line from the server. Any delimiter characters will not be included in the * result. May generate a log entry, if enabled. diff --git a/src/com/android/email/mail/store/ImapStore.java b/src/com/android/email/mail/store/ImapStore.java index 38be56db2..f47c405ad 100644 --- a/src/com/android/email/mail/store/ImapStore.java +++ b/src/com/android/email/mail/store/ImapStore.java @@ -37,6 +37,8 @@ import com.android.emailcommon.mail.Flag; import com.android.emailcommon.mail.Folder; import com.android.emailcommon.mail.Message; import com.android.emailcommon.mail.MessagingException; +import com.android.emailcommon.provider.EmailContent.Account; +import com.android.emailcommon.provider.EmailContent.HostAuth; import com.android.emailcommon.service.EmailServiceProxy; import com.android.emailcommon.utility.Utility; import com.beetstra.jutf7.CharsetProvider; @@ -51,8 +53,6 @@ import android.util.Log; import java.io.IOException; import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.security.MessageDigest; @@ -130,66 +130,53 @@ public class ImapStore extends Store { /** * Static named constructor. */ - public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks) - throws MessagingException { - return new ImapStore(context, uri); + public static Store newInstance(Account account, Context context, + PersistentDataCallbacks callbacks) throws MessagingException { + return new ImapStore(context, account); } /** - * Allowed formats for the Uri: - * imap://user:password@server:port - * imap+tls+://user:password@server:port - * imap+tls+trustallcerts://user:password@server:port - * imap+ssl+://user:password@server:port - * imap+ssl+trustallcerts://user:password@server:port - * - * @param uriString the Uri containing information to configure this store + * Creates a new store for the given account. */ - private ImapStore(Context context, String uriString) throws MessagingException { + private ImapStore(Context context, Account account) throws MessagingException { mContext = context; - URI uri; - try { - uri = new URI(uriString); - } catch (URISyntaxException use) { - throw new MessagingException("Invalid ImapStore URI", use); - } - String scheme = uri.getScheme(); - if (scheme == null || !scheme.startsWith(STORE_SCHEME_IMAP)) { + HostAuth recvAuth = account.getOrCreateHostAuthRecv(context); + if (recvAuth == null || !STORE_SCHEME_IMAP.equalsIgnoreCase(recvAuth.mProtocol)) { throw new MessagingException("Unsupported protocol"); } // defaults, which can be changed by security modifiers int connectionSecurity = Transport.CONNECTION_SECURITY_NONE; int defaultPort = 143; - // check for security modifiers and apply changes - if (scheme.contains("+ssl")) { + + // check for security flags and apply changes + if ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) { connectionSecurity = Transport.CONNECTION_SECURITY_SSL; defaultPort = 993; - } else if (scheme.contains("+tls")) { + } else if ((recvAuth.mFlags & HostAuth.FLAG_TLS) != 0) { connectionSecurity = Transport.CONNECTION_SECURITY_TLS; } - boolean trustCertificates = scheme.contains(STORE_SECURITY_TRUST_CERTIFICATES); - + boolean trustCertificates = ((recvAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0); + int port = defaultPort; + if (recvAuth.mPort != HostAuth.PORT_UNKNOWN) { + port = recvAuth.mPort; + } mRootTransport = new MailTransport("IMAP"); - mRootTransport.setUri(uri, defaultPort); + mRootTransport.setHost(recvAuth.mAddress); + mRootTransport.setPort(port); mRootTransport.setSecurity(connectionSecurity, trustCertificates); - String[] userInfoParts = mRootTransport.getUserInfoParts(); + String[] userInfoParts = recvAuth.getLogin(); if (userInfoParts != null) { mUsername = userInfoParts[0]; - if (userInfoParts.length > 1) { - mPassword = userInfoParts[1]; + mPassword = userInfoParts[1]; - // build the LOGIN string once (instead of over-and-over again.) - // apply the quoting here around the built-up password - mLoginPhrase = ImapConstants.LOGIN + " " + mUsername + " " - + ImapUtility.imapQuoted(mPassword); - } - } - - if ((uri.getPath() != null) && (uri.getPath().length() > 0)) { - mPathPrefix = uri.getPath().substring(1); + // build the LOGIN string once (instead of over-and-over again.) + // apply the quoting here around the built-up password + mLoginPhrase = ImapConstants.LOGIN + " " + mUsername + " " + + ImapUtility.imapQuoted(mPassword); } + mPathPrefix = recvAuth.mDomain; } /* package */ Collection getConnectionPoolForTest() { diff --git a/src/com/android/email/mail/store/Pop3Store.java b/src/com/android/email/mail/store/Pop3Store.java index c5af9e308..589d1a495 100644 --- a/src/com/android/email/mail/store/Pop3Store.java +++ b/src/com/android/email/mail/store/Pop3Store.java @@ -29,6 +29,8 @@ import com.android.emailcommon.mail.Folder; import com.android.emailcommon.mail.Message; import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.Folder.OpenMode; +import com.android.emailcommon.provider.EmailContent.Account; +import com.android.emailcommon.provider.EmailContent.HostAuth; import com.android.emailcommon.service.EmailServiceProxy; import com.android.emailcommon.utility.LoggingInputStream; import com.android.emailcommon.utility.Utility; @@ -39,8 +41,6 @@ import android.util.Log; import java.io.IOException; import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -86,54 +86,45 @@ public class Pop3Store extends Store { /** * Static named constructor. */ - public static Store newInstance(String uri, Context context, PersistentDataCallbacks callbacks) - throws MessagingException { - return new Pop3Store(uri); + public static Store newInstance(Account account, Context context, + PersistentDataCallbacks callbacks) throws MessagingException { + return new Pop3Store(context, account); } /** - * pop3://user:password@server:port - * pop3+tls+://user:password@server:port - * pop3+tls+trustallcerts://user:password@server:port - * pop3+ssl+://user:password@server:port - * pop3+ssl+trustallcerts://user:password@server:port - * - * @param _uri + * Creates a new store for the given account. */ - private Pop3Store(String _uri) throws MessagingException { - URI uri; - try { - uri = new URI(_uri); - } catch (URISyntaxException use) { - throw new MessagingException("Invalid Pop3Store URI", use); - } - - String scheme = uri.getScheme(); - if (scheme == null || !scheme.startsWith(STORE_SCHEME_POP3)) { + private Pop3Store(Context context, Account account) throws MessagingException { + HostAuth recvAuth = account.getOrCreateHostAuthRecv(context); + if (recvAuth == null || !STORE_SCHEME_POP3.equalsIgnoreCase(recvAuth.mProtocol)) { throw new MessagingException("Unsupported protocol"); } // defaults, which can be changed by security modifiers int connectionSecurity = Transport.CONNECTION_SECURITY_NONE; int defaultPort = 110; - // check for security modifiers and apply changes - if (scheme.contains("+ssl")) { + + // check for security flags and apply changes + if ((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) { connectionSecurity = Transport.CONNECTION_SECURITY_SSL; defaultPort = 995; - } else if (scheme.contains("+tls")) { + } else if ((recvAuth.mFlags & HostAuth.FLAG_TLS) != 0) { connectionSecurity = Transport.CONNECTION_SECURITY_TLS; } - boolean trustCertificates = scheme.contains(STORE_SECURITY_TRUST_CERTIFICATES); + boolean trustCertificates = ((recvAuth.mFlags & HostAuth.FLAG_TRUST_ALL) != 0); + int port = defaultPort; + if (recvAuth.mPort != HostAuth.PORT_UNKNOWN) { + port = recvAuth.mPort; + } mTransport = new MailTransport("POP3"); - mTransport.setUri(uri, defaultPort); + mTransport.setHost(recvAuth.mAddress); + mTransport.setPort(port); mTransport.setSecurity(connectionSecurity, trustCertificates); - String[] userInfoParts = mTransport.getUserInfoParts(); + String[] userInfoParts = recvAuth.getLogin(); if (userInfoParts != null) { mUsername = userInfoParts[0]; - if (userInfoParts.length > 1) { - mPassword = userInfoParts[1]; - } + mPassword = userInfoParts[1]; } } @@ -147,7 +138,7 @@ public class Pop3Store extends Store { } @Override - public Folder getFolder(String name) throws MessagingException { + public Folder getFolder(String name) { Folder folder = mFolders.get(name); if (folder == null) { folder = new Pop3Folder(name); @@ -157,7 +148,7 @@ public class Pop3Store extends Store { } @Override - public Folder[] getAllFolders() throws MessagingException { + public Folder[] getAllFolders() { return new Folder[] { getFolder("INBOX"), }; @@ -314,7 +305,7 @@ public class Pop3Store extends Store { } @Override - public OpenMode getMode() throws MessagingException { + public OpenMode getMode() { return OpenMode.READ_WRITE; } @@ -348,12 +339,12 @@ public class Pop3Store extends Store { } @Override - public boolean create(FolderType type) throws MessagingException { + public boolean create(FolderType type) { return false; } @Override - public boolean exists() throws MessagingException { + public boolean exists() { return mName.equalsIgnoreCase("INBOX"); } @@ -363,7 +354,7 @@ public class Pop3Store extends Store { } @Override - public int getUnreadMessageCount() throws MessagingException { + public int getUnreadMessageCount() { return -1; } @@ -608,14 +599,15 @@ public class Pop3Store extends Store { } @Override - public Message[] getMessages(MessageRetrievalListener listener) throws MessagingException { - throw new UnsupportedOperationException("Pop3Folder.getMessage(MessageRetrievalListener)"); + public Message[] getMessages(MessageRetrievalListener listener) { + throw new UnsupportedOperationException( + "Pop3Folder.getMessage(MessageRetrievalListener)"); } @Override - public Message[] getMessages(String[] uids, MessageRetrievalListener listener) - throws MessagingException { - throw new UnsupportedOperationException("Pop3Folder.getMessage(MessageRetrievalListener)"); + public Message[] getMessages(String[] uids, MessageRetrievalListener listener) { + throw new UnsupportedOperationException( + "Pop3Folder.getMessage(MessageRetrievalListener)"); } /** @@ -805,20 +797,20 @@ public class Pop3Store extends Store { } @Override - public Flag[] getPermanentFlags() throws MessagingException { + public Flag[] getPermanentFlags() { return PERMANENT_FLAGS; } @Override - public void appendMessages(Message[] messages) throws MessagingException { + public void appendMessages(Message[] messages) { } @Override - public void delete(boolean recurse) throws MessagingException { + public void delete(boolean recurse) { } @Override - public Message[] expunge() throws MessagingException { + public Message[] expunge() { return null; } @@ -847,8 +839,7 @@ public class Pop3Store extends Store { } @Override - public void copyMessages(Message[] msgs, Folder folder, MessageUpdateCallbacks callbacks) - throws MessagingException { + public void copyMessages(Message[] msgs, Folder folder, MessageUpdateCallbacks callbacks) { throw new UnsupportedOperationException("copyMessages is not supported in POP3"); } @@ -859,7 +850,7 @@ public class Pop3Store extends Store { // (mMessageCount * 58) / (mThroughputKbS * 1024 / 8) * 1000; // } - private Pop3Capabilities getCapabilities() throws IOException, MessagingException { + private Pop3Capabilities getCapabilities() throws IOException { Pop3Capabilities capabilities = new Pop3Capabilities(); try { String response = executeSimpleCommand("CAPA"); @@ -945,13 +936,13 @@ public class Pop3Store extends Store { } @Override - public Message createMessage(String uid) throws MessagingException { + public Message createMessage(String uid) { return new Pop3Message(uid, this); } } public static class Pop3Message extends MimeMessage { - public Pop3Message(String uid, Pop3Folder folder) throws MessagingException { + public Pop3Message(String uid, Pop3Folder folder) { mUid = uid; mFolder = folder; mSize = -1; diff --git a/src/com/android/email/mail/transport/MailTransport.java b/src/com/android/email/mail/transport/MailTransport.java index eedd2210d..6ae40b70e 100644 --- a/src/com/android/email/mail/transport/MailTransport.java +++ b/src/com/android/email/mail/transport/MailTransport.java @@ -98,6 +98,8 @@ public class MailTransport implements Transport { return newObject; } + @Override + @Deprecated public void setUri(URI uri, int defaultPort) { mHost = uri.getHost(); @@ -109,13 +111,24 @@ public class MailTransport implements Transport { if (uri.getUserInfo() != null) { mUserInfoParts = uri.getUserInfo().split(":", 2); } - } + @Override + @Deprecated public String[] getUserInfoParts() { return mUserInfoParts; } + @Override + public void setHost(String host) { + mHost = host; + } + + @Override + public void setPort(int port) { + mPort = port; + } + public String getHost() { return mHost; } diff --git a/src/com/android/email/mail/transport/SmtpSender.java b/src/com/android/email/mail/transport/SmtpSender.java index aa53c7a05..b2cdead52 100644 --- a/src/com/android/email/mail/transport/SmtpSender.java +++ b/src/com/android/email/mail/transport/SmtpSender.java @@ -40,6 +40,7 @@ import javax.net.ssl.SSLException; /** * This class handles all of the protocol-level aspects of sending messages via SMTP. + * TODO Remove dependence upon URI; there's no reason why we need it here */ public class SmtpSender extends Sender { @@ -65,6 +66,7 @@ public class SmtpSender extends Sender { * * @param uriString the Uri containing information to configure this sender */ + @SuppressWarnings("deprecation") private SmtpSender(Context context, String uriString) throws MessagingException { mContext = context; URI uri; diff --git a/tests/src/com/android/email/mail/StoreTests.java b/tests/src/com/android/email/mail/StoreTests.java index edc35c641..91db0f21f 100644 --- a/tests/src/com/android/email/mail/StoreTests.java +++ b/tests/src/com/android/email/mail/StoreTests.java @@ -17,7 +17,10 @@ package com.android.email.mail; import com.android.email.Email; +import com.android.email.mail.Store.StoreInfo; import com.android.emailcommon.mail.MessagingException; +import com.android.emailcommon.provider.EmailContent.Account; +import com.android.emailcommon.provider.EmailContent.HostAuth; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.MediumTest; @@ -27,83 +30,100 @@ import android.test.suitebuilder.annotation.MediumTest; */ @MediumTest public class StoreTests extends AndroidTestCase { + public void testGetStoreKey() throws MessagingException { + HostAuth testAuth = new HostAuth(); + Account testAccount = new Account(); + String testKey; - /** - * Test StoreInfo & Store lookup for POP accounts - */ - public void testStoreLookupPOP() throws MessagingException { - final String storeUri = "pop3://user:password@server.com"; - Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext()); - - assertNotNull("storeInfo null", info); - assertNotNull("scheme null", info.mScheme); - assertNotNull("classname null", info.mClassName); - assertFalse(info.mPushSupported); - assertEquals(Email.VISIBLE_LIMIT_DEFAULT, info.mVisibleLimitDefault); - assertEquals(Email.VISIBLE_LIMIT_INCREMENT, info.mVisibleLimitIncrement); - - // This will throw MessagingException if the result would have been null - Store store = Store.getInstance(storeUri, getContext(), null); - } - - /** - * Test StoreInfo & Store lookup for IMAP accounts - */ - public void testStoreLookupIMAP() throws MessagingException { - final String storeUri = "imap://user:password@server.com"; - Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext()); - - assertNotNull("storeInfo null", info); - assertNotNull("scheme null", info.mScheme); - assertNotNull("classname null", info.mClassName); - assertFalse(info.mPushSupported); - assertEquals(Email.VISIBLE_LIMIT_DEFAULT, info.mVisibleLimitDefault); - assertEquals(Email.VISIBLE_LIMIT_INCREMENT, info.mVisibleLimitIncrement); - - // This will throw MessagingException if the result would have been null - Store store = Store.getInstance(storeUri, getContext(), null); - } - - /** - * Test StoreInfo & Store lookup for EAS accounts - * TODO: EAS store will probably require implementation of Store.PersistentDataCallbacks - */ - public void testStoreLookupEAS() throws MessagingException { - final String storeUri = "eas://user:password@server.com"; - Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext()); - if (info != null) { - assertNotNull("scheme null", info.mScheme); - assertNotNull("classname null", info.mClassName); - assertTrue(info.mPushSupported); - assertEquals(-1, info.mVisibleLimitDefault); - assertEquals(-1, info.mVisibleLimitIncrement); - - // This will throw MessagingException if the result would have been null - Store store = Store.getInstance(storeUri, getContext(), null); - } else { - try { - Store store = Store.getInstance(storeUri, getContext(), null); - fail("MessagingException expected when EAS not supported"); - } catch (MessagingException me) { - // expected - fall through - } - } - } - - /** - * Test StoreInfo & Store lookup for unknown accounts - */ - public void testStoreLookupUnknown() { - final String storeUri = "bogus-scheme://user:password@server.com"; - Store.StoreInfo info = Store.StoreInfo.getStoreInfo(storeUri, getContext()); - assertNull(info); + // Make sure to set the host auth; otherwise we create entries in the hostauth db + testAccount.mHostAuthRecv = testAuth; + // No address defined; throws an exception try { - Store store = Store.getInstance(storeUri, getContext(), null); - fail("MessagingException expected from bogus URI scheme"); - } catch (MessagingException me) { - // expected - fall through + testKey = Store.getStoreKey(mContext, testAccount); + fail("MesasginException not thrown for missing address"); + } catch (MessagingException expected) { } + + // Empty address defined; throws an exception + testAuth.mAddress = " \t "; + try { + testKey = Store.getStoreKey(mContext, testAccount); + fail("MesasginException not thrown for empty address"); + } catch (MessagingException expected) { + } + + // Address defined, no login + testAuth.mAddress = "a.valid.address.com"; + testKey = Store.getStoreKey(mContext, testAccount); + assertEquals("a.valid.address.com", testKey); + + // Address & login defined + testAuth.mAddress = "address.org"; + testAuth.mLogin = "auser"; + testKey = Store.getStoreKey(mContext, testAccount); + assertEquals("address.orgauser", testKey); + } + + public void testGetStoreInfo() { + StoreInfo testInfo; + + // POP3 + testInfo = Store.StoreInfo.getStoreInfo("pop3", mContext); + assertNotNull(testInfo); + assertNotNull(testInfo.mScheme); + assertNotNull(testInfo.mClassName); + assertFalse(testInfo.mPushSupported); + assertEquals(Email.VISIBLE_LIMIT_DEFAULT, testInfo.mVisibleLimitDefault); + assertEquals(Email.VISIBLE_LIMIT_INCREMENT, testInfo.mVisibleLimitIncrement); + + // IMAP + testInfo = Store.StoreInfo.getStoreInfo("imap", mContext); + assertNotNull(testInfo); + assertNotNull(testInfo.mScheme); + assertNotNull(testInfo.mClassName); + assertFalse(testInfo.mPushSupported); + assertEquals(Email.VISIBLE_LIMIT_DEFAULT, testInfo.mVisibleLimitDefault); + assertEquals(Email.VISIBLE_LIMIT_INCREMENT, testInfo.mVisibleLimitIncrement); + + // Unknown + testInfo = Store.StoreInfo.getStoreInfo("unknownscheme", mContext); + assertNull(testInfo); + } + + public void testGetInstance() throws MessagingException { + HostAuth testAuth = new HostAuth(); + Account testAccount = new Account(); + Store testStore; + + // Make sure to set the host auth; otherwise we create entries in the hostauth db + testAccount.mHostAuthRecv = testAuth; + + // POP3 + testAuth.mAddress = "pop3.google.com"; + testAuth.mProtocol = "pop3"; + testStore = Store.getInstance(testAccount, getContext(), null); + assertEquals(1, Store.sStores.size()); + assertSame(testStore, Store.sStores.get("pop3.google.com")); + Store.sStores.clear(); + + // IMAP + testAuth.mAddress = "imap.google.com"; + testAuth.mProtocol = "imap"; + testStore = Store.getInstance(testAccount, getContext(), null); + assertEquals(1, Store.sStores.size()); + assertSame(testStore, Store.sStores.get("imap.google.com")); + Store.sStores.clear(); + + // Unknown + testAuth.mAddress = "unknown.google.com"; + testAuth.mProtocol = "unknown"; + try { + testStore = Store.getInstance(testAccount, getContext(), null); + fail("Store#getInstance() should have thrown an exception"); + } catch (MessagingException expected) { + } + assertEquals(0, Store.sStores.size()); } } diff --git a/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java b/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java index 2e8c00920..b291eeaf3 100644 --- a/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java +++ b/tests/src/com/android/email/mail/store/ImapStoreUnitTests.java @@ -41,12 +41,15 @@ import com.android.emailcommon.mail.Message; import com.android.emailcommon.mail.Message.RecipientType; import com.android.emailcommon.mail.MessagingException; import com.android.emailcommon.mail.Part; +import com.android.emailcommon.provider.EmailContent.Account; +import com.android.emailcommon.provider.EmailContent.HostAuth; import com.android.emailcommon.utility.Utility; import org.apache.commons.io.IOUtils; +import android.content.Context; import android.os.Bundle; -import android.test.AndroidTestCase; +import android.test.InstrumentationTestCase; import android.test.MoreAsserts; import android.test.suitebuilder.annotation.SmallTest; @@ -66,7 +69,7 @@ import java.util.regex.Pattern; * TODO test for BYE response in various places? */ @SmallTest -public class ImapStoreUnitTests extends AndroidTestCase { +public class ImapStoreUnitTests extends InstrumentationTestCase { private final static String[] NO_REPLY = new String[0]; /** @@ -85,6 +88,7 @@ public class ImapStoreUnitTests extends AndroidTestCase { /* These values are provided by setUp() */ private ImapStore mStore = null; private ImapFolder mFolder = null; + private Context mTestContext; private int mNextTag; // Fields specific to the CopyMessages tests @@ -98,11 +102,18 @@ public class ImapStoreUnitTests extends AndroidTestCase { @Override protected void setUp() throws Exception { super.setUp(); - TempDirectory.setTempDirectory(getContext()); + mTestContext = getInstrumentation().getContext(); + // Use the target's (i.e. the Email application) context + TempDirectory.setTempDirectory(getInstrumentation().getTargetContext()); // These are needed so we can get at the inner classes - mStore = (ImapStore) ImapStore.newInstance("imap://user:password@server:999", - getContext(), null); + HostAuth testAuth = new HostAuth(); + Account testAccount = new Account(); + + testAuth.setLogin("user", "password"); + testAuth.setConnection("imap", "server", 999); + testAccount.mHostAuthRecv = testAuth; + mStore = (ImapStore) ImapStore.newInstance(testAccount, mTestContext, null); mFolder = (ImapFolder) mStore.getFolder(FOLDER_NAME); mNextTag = 1; } @@ -194,10 +205,10 @@ public class ImapStoreUnitTests extends AndroidTestCase { // x-android-device-model Model (Optional, so not tested here) // x-android-net-operator Carrier (Unreliable, so not tested here) // AGUID A device+account UID - String id = ImapStore.getImapId(getContext(), "user-name", "host-name", + String id = ImapStore.getImapId(mTestContext, "user-name", "host-name", CAPABILITY_RESPONSE.flatten()); HashMap map = tokenizeImapId(id); - assertEquals(getContext().getPackageName(), map.get("name")); + assertEquals(mTestContext.getPackageName(), map.get("name")); assertEquals("android", map.get("os")); assertNotNull(map.get("os-version")); assertNotNull(map.get("vendor")); @@ -237,7 +248,7 @@ public class ImapStoreUnitTests extends AndroidTestCase { */ public void testImapIdWithVendorPolicy() { try { - MockVendorPolicy.inject(getContext()); + MockVendorPolicy.inject(mTestContext); // Prepare mock result Bundle result = new Bundle(); @@ -245,7 +256,7 @@ public class ImapStoreUnitTests extends AndroidTestCase { MockVendorPolicy.mockResult = result; // Invoke - String id = ImapStore.getImapId(getContext(), "user-name", "host-name", + String id = ImapStore.getImapId(mTestContext, "user-name", "host-name", ImapTestUtils.parseResponse("* CAPABILITY IMAP4rev1 XXX YYY Z").flatten()); // Check the result @@ -289,17 +300,37 @@ public class ImapStoreUnitTests extends AndroidTestCase { * Test that IMAP ID uid's are per-username */ public void testImapIdDeviceId() throws MessagingException { - ImapStore store1a = (ImapStore) ImapStore.newInstance("imap://user1:password@server:999", - getContext(), null); - ImapStore store1b = (ImapStore) ImapStore.newInstance("imap://user1:password@server:999", - getContext(), null); - ImapStore store2 = (ImapStore) ImapStore.newInstance("imap://user2:password@server:999", - getContext(), null); + HostAuth testAuth; + Account testAccount; + + // store 1a + testAuth = new HostAuth(); + testAuth.setLogin("user1", "password"); + testAuth.setConnection("imap", "server", 999); + testAccount = new Account(); + testAccount.mHostAuthRecv = testAuth; + ImapStore testStore1A = (ImapStore) ImapStore.newInstance(testAccount, mTestContext, null); + + // store 1b + testAuth = new HostAuth(); + testAuth.setLogin("user1", "password"); + testAuth.setConnection("imap", "server", 999); + testAccount = new Account(); + testAccount.mHostAuthRecv = testAuth; + ImapStore testStore1B = (ImapStore) ImapStore.newInstance(testAccount, mTestContext, null); + + // store 2 + testAuth = new HostAuth(); + testAuth.setLogin("user2", "password"); + testAuth.setConnection("imap", "server", 999); + testAccount = new Account(); + testAccount.mHostAuthRecv = testAuth; + ImapStore testStore2 = (ImapStore) ImapStore.newInstance(testAccount, mTestContext, null); String capabilities = CAPABILITY_RESPONSE.flatten(); - String id1a = ImapStore.getImapId(getContext(), "user1", "host-name", capabilities); - String id1b = ImapStore.getImapId(getContext(), "user1", "host-name", capabilities); - String id2 = ImapStore.getImapId(getContext(), "user2", "host-name", capabilities); + String id1a = ImapStore.getImapId(mTestContext, "user1", "host-name", capabilities); + String id1b = ImapStore.getImapId(mTestContext, "user1", "host-name", capabilities); + String id2 = ImapStore.getImapId(mTestContext, "user2", "host-name", capabilities); String uid1a = tokenizeImapId(id1a).get("AGUID"); String uid1b = tokenizeImapId(id1b).get("AGUID"); @@ -412,7 +443,7 @@ public class ImapStoreUnitTests extends AndroidTestCase { public void testImapIdSecureServerNotSent() throws MessagingException { // Note, this is injected into mStore (which we don't use for this test) MockTransport mockTransport = openAndInjectMockTransport(); - mockTransport.setMockHost("eMail.sEcurEserVer.nEt"); + mockTransport.setHost("eMail.sEcurEserVer.nEt"); // Prime the expects pump as if the server wants IMAP ID, but we should not actually expect // to send it, because the login code in the store should never actually send it (to this @@ -498,7 +529,7 @@ public class ImapStoreUnitTests extends AndroidTestCase { // Create mock transport and inject it into the ImapStore that's already set up MockTransport mockTransport = new MockTransport(); mockTransport.setSecurity(connectionSecurity, trustAllCertificates); - mockTransport.setMockHost("mock.server.com"); + mockTransport.setHost("mock.server.com"); mStore.setTransport(mockTransport); return mockTransport; } diff --git a/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java b/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java index 9417bbd48..e6276fb1a 100644 --- a/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java +++ b/tests/src/com/android/email/mail/store/Pop3StoreUnitTests.java @@ -29,8 +29,10 @@ import com.android.emailcommon.mail.Folder.OpenMode; import com.android.emailcommon.mail.Message; import com.android.emailcommon.mail.Message.RecipientType; import com.android.emailcommon.mail.MessagingException; +import com.android.emailcommon.provider.EmailContent.Account; +import com.android.emailcommon.provider.EmailContent.HostAuth; -import android.test.AndroidTestCase; +import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.SmallTest; /** @@ -38,27 +40,33 @@ import android.test.suitebuilder.annotation.SmallTest; * complete - no server(s) required. */ @SmallTest -public class Pop3StoreUnitTests extends AndroidTestCase { - +public class Pop3StoreUnitTests extends InstrumentationTestCase { final String UNIQUE_ID_1 = "20080909002219r1800rrjo9e00"; - + final static int PER_MESSAGE_SIZE = 100; - + /* These values are provided by setUp() */ private Pop3Store mStore = null; private Pop3Store.Pop3Folder mFolder = null; - + /** * Setup code. We generate a lightweight Pop3Store and Pop3Store.Pop3Folder. */ @Override protected void setUp() throws Exception { super.setUp(); - TempDirectory.setTempDirectory(getContext()); + // Use the target's (i.e. the Email application) context + TempDirectory.setTempDirectory(getInstrumentation().getTargetContext()); // These are needed so we can get at the inner classes - mStore = (Pop3Store) Pop3Store.newInstance("pop3://user:password@server:999", - getContext(), null); + HostAuth testAuth = new HostAuth(); + Account testAccount = new Account(); + + testAuth.setLogin("user", "password"); + testAuth.setConnection("pop3", "server", 999); + testAccount.mHostAuthRecv = testAuth; + mStore = (Pop3Store) Pop3Store.newInstance( + testAccount, getInstrumentation().getContext(), null); mFolder = (Pop3Store.Pop3Folder) mStore.getFolder("INBOX"); } @@ -69,24 +77,24 @@ public class Pop3StoreUnitTests extends AndroidTestCase { // multi-line mode Pop3Store.Pop3Folder.UidlParser parser = mFolder.new UidlParser(); - + // Test basic in-list UIDL parser.parseMultiLine("101 " + UNIQUE_ID_1); assertEquals(101, parser.mMessageNumber); assertEquals(UNIQUE_ID_1, parser.mUniqueId); assertFalse(parser.mEndOfMessage); assertFalse(parser.mErr); - + // Test end-of-list parser.parseMultiLine("."); assertTrue(parser.mEndOfMessage); assertFalse(parser.mErr); } - + /** * Test various sunny-day operations of UIDL parser for single-line responses */ - public void testUIDLParserSingle() { + public void testUIDLParserSingle() { // single-line mode Pop3Store.Pop3Folder.UidlParser parser = mFolder.new UidlParser(); @@ -96,12 +104,12 @@ public class Pop3StoreUnitTests extends AndroidTestCase { assertEquals(101, parser.mMessageNumber); assertEquals(UNIQUE_ID_1, parser.mUniqueId); assertTrue(parser.mEndOfMessage); - + // Test single-message ERR response parser.parseSingleLine("-ERR what???"); assertTrue(parser.mErr); } - + /** * Test various rainy-day operations of the UIDL parser for multi-line responses * TODO other malformed responses @@ -109,17 +117,17 @@ public class Pop3StoreUnitTests extends AndroidTestCase { public void testUIDLParserMultiFail() { // multi-line mode Pop3Store.Pop3Folder.UidlParser parser = mFolder.new UidlParser(); - + // Test with null input boolean result; result = parser.parseMultiLine(null); assertFalse(result); - + // Test with empty input result = parser.parseMultiLine(""); assertFalse(result); } - + /** * Test various rainy-day operations of the UIDL parser for single-line responses * TODO other malformed responses @@ -127,25 +135,25 @@ public class Pop3StoreUnitTests extends AndroidTestCase { public void testUIDLParserSingleFail() { // single-line mode Pop3Store.Pop3Folder.UidlParser parser = mFolder.new UidlParser(); - + // Test with null input boolean result; result = parser.parseSingleLine(null); assertFalse(result); - + // Test with empty input result = parser.parseSingleLine(""); assertFalse(result); } - + /** * Tests that variants on the RFC-specified formatting of UIDL work properly. */ public void testUIDLComcastVariant() { - + // multi-line mode Pop3Store.Pop3Folder.UidlParser parser = mFolder.new UidlParser(); - + // Comcast servers send multiple spaces in their darn UIDL strings. parser.parseMultiLine("101 " + UNIQUE_ID_1); assertEquals(101, parser.mMessageNumber); @@ -153,19 +161,19 @@ public class Pop3StoreUnitTests extends AndroidTestCase { assertFalse(parser.mEndOfMessage); assertFalse(parser.mErr); } - + /** * Confirms simple non-SSL non-TLS login */ public void testSimpleLogin() throws MessagingException { - + MockTransport mockTransport = openAndInjectMockTransport(); - + // try to open it setupOpenFolder(mockTransport, 0, null); mFolder.open(OpenMode.READ_ONLY, null); } - + /** * TODO: Test with SSL negotiation (faked) * TODO: Test with SSL required but not supported @@ -173,25 +181,25 @@ public class Pop3StoreUnitTests extends AndroidTestCase { * TODO: Test with TLS required but not supported * TODO: Test calling getMessageCount(), getMessages(), etc. */ - + /** * Test the operation of checkSettings(), which requires (a) a good open and (b) UIDL support. */ public void testCheckSettings() throws MessagingException { MockTransport mockTransport = openAndInjectMockTransport(); - + // scenario 1: CAPA returns -ERR, so we try UIDL explicitly setupOpenFolder(mockTransport, 0, null); setupUidlSequence(mockTransport, 1); mockTransport.expect("QUIT", ""); mStore.checkSettings(); - + // scenario 2: CAPA indicates UIDL, so we don't try UIDL setupOpenFolder(mockTransport, 0, "UIDL"); mockTransport.expect("QUIT", ""); mStore.checkSettings(); - + // scenario 3: CAPA returns -ERR, and UIDL fails try { setupOpenFolder(mockTransport, 0, null); @@ -238,8 +246,8 @@ public class Pop3StoreUnitTests extends AndroidTestCase { /** * Test small Store & Folder functions that manage folders & namespace */ - public void testStoreFoldersFunctions() throws MessagingException { - + public void testStoreFoldersFunctions() { + // getPersonalNamespaces() always returns INBOX folder Folder[] folders = mStore.getAllFolders(); assertEquals(1, folders.length); @@ -251,18 +259,18 @@ public class Pop3StoreUnitTests extends AndroidTestCase { assertEquals("INBOX", folderMixedCaseInbox.getName()); Pop3Store.Pop3Folder folderNotInbox = mStore.new Pop3Folder("NOT-INBOX"); assertEquals("NOT-INBOX", folderNotInbox.getName()); - + // exists() true if name is INBOX assertTrue(mFolder.exists()); assertTrue(folderMixedCaseInbox.exists()); assertFalse(folderNotInbox.exists()); } - + /** * Test small Folder functions that don't really do anything in Pop3 */ - public void testSmallFolderFunctions() throws MessagingException { - + public void testSmallFolderFunctions() { + // getMode() returns OpenMode.READ_WRITE assertEquals(OpenMode.READ_WRITE, mFolder.getMode()); @@ -274,7 +282,7 @@ public class Pop3StoreUnitTests extends AndroidTestCase { // getUnreadMessageCount() always returns -1 assertEquals(-1, mFolder.getUnreadMessageCount()); - + // getMessages(MessageRetrievalListener listener) is unsupported try { mFolder.getMessages(null); @@ -282,7 +290,7 @@ public class Pop3StoreUnitTests extends AndroidTestCase { } catch (UnsupportedOperationException e) { // expected - succeed } - + // getMessages(String[] uids, MessageRetrievalListener listener) is unsupported try { mFolder.getMessages(null, null); @@ -290,22 +298,22 @@ public class Pop3StoreUnitTests extends AndroidTestCase { } catch (UnsupportedOperationException e) { // expected - succeed } - + // getPermanentFlags() returns { Flag.DELETED } Flag[] flags = mFolder.getPermanentFlags(); assertEquals(1, flags.length); assertEquals(Flag.DELETED, flags[0]); - + // appendMessages(Message[] messages) does nothing mFolder.appendMessages(null); - + // delete(boolean recurse) does nothing // TODO - it should! mFolder.delete(false); - + // expunge() returns null assertNull(mFolder.expunge()); - + // copyMessages() is unsupported try { mFolder.copyMessages(null, null, null); @@ -314,14 +322,14 @@ public class Pop3StoreUnitTests extends AndroidTestCase { // expected - succeed } } - + /** * Lightweight test to confirm that POP3 hasn't implemented any folder roles yet. */ - public void testNoFolderRolesYet() throws MessagingException { + public void testNoFolderRolesYet() { Folder[] remoteFolders = mStore.getAllFolders(); for (Folder folder : remoteFolders) { - assertEquals(Folder.FolderRole.UNKNOWN, folder.getRole()); + assertEquals(Folder.FolderRole.UNKNOWN, folder.getRole()); } } @@ -329,26 +337,26 @@ public class Pop3StoreUnitTests extends AndroidTestCase { * Lightweight test to confirm that POP3 isn't requesting structure prefetch. */ public void testNoStructurePrefetch() { - assertFalse(mStore.requireStructurePrefetch()); + assertFalse(mStore.requireStructurePrefetch()); } - + /** * Lightweight test to confirm that POP3 is requesting sent-message-upload. */ public void testSentUploadRequested() { - assertTrue(mStore.requireCopyMessageToSentFolder()); + assertTrue(mStore.requireCopyMessageToSentFolder()); } /** * Test the process of opening and indexing a mailbox with one unread message in it. - * + * * TODO should create an instrumented listener to confirm all expected callbacks. Then use * it everywhere we could have passed a message listener. */ public void testOneUnread() throws MessagingException { - + MockTransport mockTransport = openAndInjectMockTransport(); - + checkOneUnread(mockTransport); } @@ -356,9 +364,9 @@ public class Pop3StoreUnitTests extends AndroidTestCase { * Test the process of opening and getting message by uid. */ public void testGetMessageByUid() throws MessagingException { - + MockTransport mockTransport = openAndInjectMockTransport(); - + setupOpenFolder(mockTransport, 2, null); mFolder.open(OpenMode.READ_WRITE, null); // check message count @@ -369,7 +377,7 @@ public class Pop3StoreUnitTests extends AndroidTestCase { String uid1 = getSingleMessageUID(1); String uid2 = getSingleMessageUID(2); String uid3 = getSingleMessageUID(3); - + Message msg1 = mFolder.getMessage(uid1); assertTrue("message with uid1", msg1 != null); @@ -385,11 +393,11 @@ public class Pop3StoreUnitTests extends AndroidTestCase { * Test the scenario where the transport is "open" but not really (e.g. server closed). Two * things should happen: We should see an intermediate failure that makes sense, and the next * operation should reopen properly. - * + * * There are multiple versions of this test because we are simulating the steps of * MessagingController.synchronizeMailboxSyncronous() and we will inject the failure a bit * further along in each case, to test various recovery points. - * + * * This test confirms that Pop3Store needs to call close() in the IOExceptionHandler in * Pop3Folder.getMessages(), due to a closure before the UIDL command completes. */ @@ -428,11 +436,11 @@ public class Pop3StoreUnitTests extends AndroidTestCase { * Test the scenario where the transport is "open" but not really (e.g. server closed). Two * things should happen: We should see an intermediate failure that makes sense, and the next * operation should reopen properly. - * + * * There are multiple versions of this test because we are simulating the steps of * MessagingController.synchronizeMailboxSyncronous() and we will inject the failure a bit * further along in each case, to test various recovery points. - * + * * This test confirms that Pop3Store needs to call close() in the IOExceptionHandler in * Pop3Folder.getMessages(), due to non-numeric data in a multi-line UIDL. */ @@ -470,11 +478,11 @@ public class Pop3StoreUnitTests extends AndroidTestCase { * Test the scenario where the transport is "open" but not really (e.g. server closed). Two * things should happen: We should see an intermediate failure that makes sense, and the next * operation should reopen properly. - * + * * There are multiple versions of this test because we are simulating the steps of * MessagingController.synchronizeMailboxSyncronous() and we will inject the failure a bit * further along in each case, to test various recovery points. - * + * * This test confirms that Pop3Store needs to call close() in the IOExceptionHandler in * Pop3Folder.getMessages(), due to non-numeric data in a single-line UIDL. */ @@ -513,29 +521,29 @@ public class Pop3StoreUnitTests extends AndroidTestCase { * Test the scenario where the transport is "open" but not really (e.g. server closed). Two * things should happen: We should see an intermediate failure that makes sense, and the next * operation should reopen properly. - * - * There are multiple versions of this test because we are simulating the steps of + * + * There are multiple versions of this test because we are simulating the steps of * MessagingController.synchronizeMailboxSyncronous() and we will inject the failure a bit * further along in each case, to test various recovery points. - * - * This test confirms that Pop3Store needs to call close() in the first IOExceptionHandler in + * + * This test confirms that Pop3Store needs to call close() in the first IOExceptionHandler in * Pop3Folder.fetch(), for a failure in the call to indexUids(). */ public void testCatchClosed2() throws MessagingException { - + MockTransport mockTransport = openAndInjectMockTransport(); - + openFolderWithMessage(mockTransport); - + // index the message(s) setupUidlSequence(mockTransport, 1); Message[] messages = mFolder.getMessages(1, 1, null); assertEquals(1, messages.length); - assertEquals(getSingleMessageUID(1), messages[0].getUid()); - + assertEquals(getSingleMessageUID(1), messages[0].getUid()); + // cause the next sequence to fail on the readLine() calls mockTransport.closeInputStream(); - + try { // try the basic fetch of flags & envelope setupListSequence(mockTransport, 1); @@ -553,36 +561,36 @@ public class Pop3StoreUnitTests extends AndroidTestCase { // At this point the UI would display connection error, which is fine. Now, the real // test is, can we recover? So I'll just repeat the above steps, without the failure. // NOTE: everything from here down is copied from testOneUnread() and should be consolidated - + // confirm that we're closed at this point assertFalse("folder should be 'closed' after an IOError", mFolder.isOpen()); - + // and confirm that the next connection will be OK checkOneUnread(mockTransport); } - + /** * Test the scenario where the transport is "open" but not really (e.g. server closed). Two * things should happen: We should see an intermediate failure that makes sense, and the next * operation should reopen properly. - * + * * There are multiple versions of this test because we have to check additional places where * Pop3Store and/or Pop3Folder should be dealing with IOErrors. - * - * This test confirms that Pop3Store needs to call close() in the first IOExceptionHandler in + * + * This test confirms that Pop3Store needs to call close() in the first IOExceptionHandler in * Pop3Folder.fetch(), for a failure in the call to fetchEnvelope(). */ public void testCatchClosed2a() throws MessagingException { - + MockTransport mockTransport = openAndInjectMockTransport(); - + openFolderWithMessage(mockTransport); - + // index the message(s) setupUidlSequence(mockTransport, 1); Message[] messages = mFolder.getMessages(1, 1, null); assertEquals(1, messages.length); - assertEquals(getSingleMessageUID(1), messages[0].getUid()); + assertEquals(getSingleMessageUID(1), messages[0].getUid()); // try the basic fetch of flags & envelope, but the LIST command fails setupBrokenListSequence(mockTransport, 1); @@ -600,37 +608,37 @@ public class Pop3StoreUnitTests extends AndroidTestCase { // At this point the UI would display connection error, which is fine. Now, the real // test is, can we recover? So I'll just repeat the above steps, without the failure. // NOTE: everything from here down is copied from testOneUnread() and should be consolidated - + // confirm that we're closed at this point assertFalse("folder should be 'closed' after an IOError", mFolder.isOpen()); - + // and confirm that the next connection will be OK checkOneUnread(mockTransport); } - + /** * Test the scenario where the transport is "open" but not really (e.g. server closed). Two * things should happen: We should see an intermediate failure that makes sense, and the next * operation should reopen properly. - * - * There are multiple versions of this test because we are simulating the steps of + * + * There are multiple versions of this test because we are simulating the steps of * MessagingController.synchronizeMailboxSyncronous() and we will inject the failure a bit * further along in each case, to test various recovery points. - * - * This test confirms that Pop3Store needs to call close() in the second IOExceptionHandler in + * + * This test confirms that Pop3Store needs to call close() in the second IOExceptionHandler in * Pop3Folder.fetch(). */ public void testCatchClosed3() throws MessagingException { - + MockTransport mockTransport = openAndInjectMockTransport(); - + openFolderWithMessage(mockTransport); - + // index the message(s) setupUidlSequence(mockTransport, 1); Message[] messages = mFolder.getMessages(1, 1, null); assertEquals(1, messages.length); - assertEquals(getSingleMessageUID(1), messages[0].getUid()); + assertEquals(getSingleMessageUID(1), messages[0].getUid()); // try the basic fetch of flags & envelope setupListSequence(mockTransport, 1); @@ -659,37 +667,37 @@ public class Pop3StoreUnitTests extends AndroidTestCase { // At this point the UI would display connection error, which is fine. Now, the real // test is, can we recover? So I'll just repeat the above steps, without the failure. // NOTE: everything from here down is copied from testOneUnread() and should be consolidated - + // confirm that we're closed at this point assertFalse("folder should be 'closed' after an IOError", mFolder.isOpen()); - + // and confirm that the next connection will be OK checkOneUnread(mockTransport); } - + /** * Test the scenario where the transport is "open" but not really (e.g. server closed). Two * things should happen: We should see an intermediate failure that makes sense, and the next * operation should reopen properly. - * + * * There are multiple versions of this test because we have to check additional places where * Pop3Store and/or Pop3Folder should be dealing with IOErrors. - * - * This test confirms that Pop3Store needs to call close() in the IOExceptionHandler in + * + * This test confirms that Pop3Store needs to call close() in the IOExceptionHandler in * Pop3Folder.setFlags(). */ public void testCatchClosed4() throws MessagingException { - + MockTransport mockTransport = openAndInjectMockTransport(); - + openFolderWithMessage(mockTransport); - + // index the message(s) setupUidlSequence(mockTransport, 1); Message[] messages = mFolder.getMessages(1, 1, null); assertEquals(1, messages.length); assertEquals(getSingleMessageUID(1), messages[0].getUid()); - + // cause the next sequence to fail on the readLine() calls mockTransport.closeInputStream(); @@ -706,44 +714,44 @@ public class Pop3StoreUnitTests extends AndroidTestCase { // At this point the UI would display connection error, which is fine. Now, the real // test is, can we recover? So I'll just repeat the above steps, without the failure. // NOTE: everything from here down is copied from testOneUnread() and should be consolidated - + // confirm that we're closed at this point assertFalse("folder should be 'closed' after an IOError", mFolder.isOpen()); - + // and confirm that the next connection will be OK checkOneUnread(mockTransport); } - + /** * Test the scenario where the transport is "open" but not really (e.g. server closed). Two * things should happen: We should see an intermediate failure that makes sense, and the next * operation should reopen properly. - * + * * There are multiple versions of this test because we have to check additional places where * Pop3Store and/or Pop3Folder should be dealing with IOErrors. - * - * This test confirms that Pop3Store needs to call close() in the first IOExceptionHandler in + * + * This test confirms that Pop3Store needs to call close() in the first IOExceptionHandler in * Pop3Folder.open(). */ public void testCatchClosed5() { // TODO cannot write this test until we can inject stream closures mid-sequence } - + /** * Test the scenario where the transport is "open" but not really (e.g. server closed). Two * things should happen: We should see an intermediate failure that makes sense, and the next * operation should reopen properly. - * + * * There are multiple versions of this test because we have to check additional places where * Pop3Store and/or Pop3Folder should be dealing with IOErrors. - * - * This test confirms that Pop3Store needs to call close() in the second IOExceptionHandler in + * + * This test confirms that Pop3Store needs to call close() in the second IOExceptionHandler in * Pop3Folder.open() (when it calls STAT and the response is empty of garbagey). */ public void testCatchClosed6a() throws MessagingException { - + MockTransport mockTransport = openAndInjectMockTransport(); - + // like openFolderWithMessage(mockTransport) but with a broken STAT report (empty response) setupOpenFolder(mockTransport, -1, null); try { @@ -752,48 +760,48 @@ public class Pop3StoreUnitTests extends AndroidTestCase { } catch(MessagingException me) { // success } - + // At this point the UI would display connection error, which is fine. Now, the real // test is, can we recover? So I'll try a new connection, without the failure. - + // confirm that we're closed at this point assertFalse("folder should be 'closed' after an IOError", mFolder.isOpen()); - + // and confirm that the next connection will be OK checkOneUnread(mockTransport); } - + /** * Test the scenario where the transport is "open" but not really (e.g. server closed). Two * things should happen: We should see an intermediate failure that makes sense, and the next * operation should reopen properly. - * + * * There are multiple versions of this test because we have to check additional places where * Pop3Store and/or Pop3Folder should be dealing with IOErrors. - * - * This test confirms that Pop3Store needs to call close() in the second IOExceptionHandler in + * + * This test confirms that Pop3Store needs to call close() in the second IOExceptionHandler in * Pop3Folder.open() (when it calls STAT, and there is no response at all). */ - public void testCatchClosed6b() throws MessagingException { + public void testCatchClosed6b() { // TODO cannot write this test until we can inject stream closures mid-sequence } - + /** - * Given an initialized mock transport, open it and attempt to "read" one unread message from + * Given an initialized mock transport, open it and attempt to "read" one unread message from * it. This can be used as a basic test of functionality and it should be possible to call this * repeatedly (if you close the folder between calls). - * + * * @param mockTransport the mock transport we're using */ private void checkOneUnread(MockTransport mockTransport) throws MessagingException { openFolderWithMessage(mockTransport); - + // index the message(s) setupUidlSequence(mockTransport, 1); Message[] messages = mFolder.getMessages(1, 1, null); assertEquals(1, messages.length); assertEquals(getSingleMessageUID(1), messages[0].getUid()); - + // try the basic fetch of flags & envelope setupListSequence(mockTransport, 1); FetchProfile fp = new FetchProfile(); @@ -801,8 +809,8 @@ public class Pop3StoreUnitTests extends AndroidTestCase { fp.add(FetchProfile.Item.ENVELOPE); mFolder.fetch(messages, fp, null); assertEquals(PER_MESSAGE_SIZE, messages[0].getSize()); - - // A side effect of how messages work is that if you get fields that are empty, + + // A side effect of how messages work is that if you get fields that are empty, // then empty arrays are written back into the parsed header fields (e.g. mTo, mFrom). The // standard message parser needs to clear these before parsing. Make sure that this // is happening. (This doesn't affect IMAP, which reads the headers directly via @@ -841,7 +849,7 @@ public class Pop3StoreUnitTests extends AndroidTestCase { fp.add(FetchProfile.Item.ENVELOPE); mFolder.fetch(messages, fp, null); - // A side effect of how messages work is that if you get fields that are empty, + // A side effect of how messages work is that if you get fields that are empty, // then empty arrays are written back into the parsed header fields (e.g. mTo, mFrom). The // standard message parser needs to clear these before parsing. Make sure that this // is happening. (This doesn't affect IMAP, which reads the headers directly via @@ -881,30 +889,30 @@ public class Pop3StoreUnitTests extends AndroidTestCase { mStore.setTransport(mockTransport); return mockTransport; } - + /** * Open a folder that's preloaded with one unread message. - * + * * @param mockTransport the mock transport we're using */ private void openFolderWithMessage(MockTransport mockTransport) throws MessagingException { // try to open it setupOpenFolder(mockTransport, 1, null); mFolder.open(OpenMode.READ_ONLY, null); - + // check message count assertEquals(1, mFolder.getMessageCount()); } - + /** * Look at a fetched message and confirm that it is complete. - * + * * TODO this needs to be more dynamic, not just hardcoded for empty message #1. - * + * * @param message the fetched message to be checked * @param msgNum the message number */ - private void checkFetchedMessage(Message message, int msgNum, boolean body) + private void checkFetchedMessage(Message message, int msgNum, boolean body) throws MessagingException { // check To: Address[] to = message.getRecipients(RecipientType.TO); @@ -912,14 +920,14 @@ public class Pop3StoreUnitTests extends AndroidTestCase { assertEquals(1, to.length); assertEquals("Smith@Registry.Org", to[0].getAddress()); assertNull(to[0].getPersonal()); - + // check From: Address[] from = message.getFrom(); assertNotNull(from); assertEquals(1, from.length); assertEquals("Jones@Registry.Org", from[0].getAddress()); assertNull(from[0].getPersonal()); - + // check Cc: Address[] cc = message.getRecipients(RecipientType.CC); assertNotNull(cc); @@ -935,13 +943,13 @@ public class Pop3StoreUnitTests extends AndroidTestCase { assertNull(replyto[0].getPersonal()); // TODO date - + // TODO check body (if applicable) } /** * Helper which stuffs the mock with enough strings to satisfy a call to Pop3Folder.open() - * + * * @param mockTransport the mock transport we're using * @param statCount the number of messages to indicate in the STAT, or -1 for broken STAT * @param capabilities if non-null, comma-separated list of capabilities @@ -972,7 +980,7 @@ public class Pop3StoreUnitTests extends AndroidTestCase { * @param numMessages The number of messages to return from UIDL. */ private static void setupUidlSequence(MockTransport transport, int numMessages) { - transport.expect("UIDL", "+OK sending UIDL list"); + transport.expect("UIDL", "+OK sending UIDL list"); for (int msgNum = 1; msgNum <= numMessages; ++msgNum) { transport.expect(null, Integer.toString(msgNum) + " " + getSingleMessageUID(msgNum)); } @@ -985,9 +993,9 @@ public class Pop3StoreUnitTests extends AndroidTestCase { * @param numMessages The number of messages to return from LIST. */ private static void setupListSequence(MockTransport transport, int numMessages) { - transport.expect("LIST", "+OK sending scan listing"); + transport.expect("LIST", "+OK sending scan listing"); for (int msgNum = 1; msgNum <= numMessages; ++msgNum) { - transport.expect(null, Integer.toString(msgNum) + " " + + transport.expect(null, Integer.toString(msgNum) + " " + Integer.toString(PER_MESSAGE_SIZE * msgNum)); } transport.expect(null, "."); @@ -1009,16 +1017,16 @@ public class Pop3StoreUnitTests extends AndroidTestCase { /** * Setup a single message to be retrieved. - * + * * Per RFC822 here is a minimal message header: * Date: 26 Aug 76 1429 EDT * From: Jones@Registry.Org * To: Smith@Registry.Org - * + * * We'll add the following fields to support additional tests: * Cc: Chris@Registry.Org * Reply-To: Roger@Registry.Org - * + * * @param transport the mock transport to preload * @param msgNum the message number to expect and return * @param body if true, a non-empty body will be added diff --git a/tests/src/com/android/email/mail/transport/MockTransport.java b/tests/src/com/android/email/mail/transport/MockTransport.java index 9b9a2b069..97711b997 100644 --- a/tests/src/com/android/email/mail/transport/MockTransport.java +++ b/tests/src/com/android/email/mail/transport/MockTransport.java @@ -204,11 +204,8 @@ public class MockTransport implements Transport { mPairs.clear(); } - /** - * This is a test function (not part of the interface) and is used to set up a result - * value for getHost(), if needed for the test. - */ - public void setMockHost(String host) { + @Override + public void setHost(String host) { mHost = host; } @@ -239,6 +236,11 @@ public class MockTransport implements Transport { return new MockOutputStream(); } + @Override + public void setPort(int port) { + SmtpSenderUnitTests.fail("setPort() not implemented"); + } + public int getPort() { SmtpSenderUnitTests.fail("getPort() not implemented"); return 0; @@ -248,6 +250,7 @@ public class MockTransport implements Transport { return mConnectionSecurity; } + @Deprecated public String[] getUserInfoParts() { SmtpSenderUnitTests.fail("getUserInfoParts() not implemented"); return null; @@ -315,6 +318,7 @@ public class MockTransport implements Transport { public void setSoTimeout(int timeoutMilliseconds) /* throws SocketException */ { } + @Deprecated public void setUri(URI uri, int defaultPort) { SmtpSenderUnitTests.assertTrue("Don't call setUri on a mock transport", false); } @@ -413,4 +417,4 @@ public class MockTransport implements Transport { return null; } } -} \ No newline at end of file +}