Home | History | Annotate | Download | only in provider
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.email.provider;
     18 
     19 import android.accounts.AccountManager;
     20 import android.accounts.AuthenticatorException;
     21 import android.accounts.OperationCanceledException;
     22 import android.content.ContentResolver;
     23 import android.content.ContentUris;
     24 import android.content.ContentValues;
     25 import android.content.Context;
     26 import android.content.ContextWrapper;
     27 import android.database.Cursor;
     28 import android.database.sqlite.SQLiteDatabase;
     29 import android.net.Uri;
     30 import android.os.Bundle;
     31 import android.os.Environment;
     32 import android.os.Parcel;
     33 import android.test.MoreAsserts;
     34 import android.test.ProviderTestCase2;
     35 import android.test.suitebuilder.annotation.LargeTest;
     36 import android.test.suitebuilder.annotation.MediumTest;
     37 import android.test.suitebuilder.annotation.SmallTest;
     38 
     39 import com.android.email.provider.EmailProvider.AttachmentService;
     40 import com.android.emailcommon.AccountManagerTypes;
     41 import com.android.emailcommon.provider.Account;
     42 import com.android.emailcommon.provider.EmailContent;
     43 import com.android.emailcommon.provider.EmailContent.AccountColumns;
     44 import com.android.emailcommon.provider.EmailContent.Attachment;
     45 import com.android.emailcommon.provider.EmailContent.AttachmentColumns;
     46 import com.android.emailcommon.provider.EmailContent.Body;
     47 import com.android.emailcommon.provider.EmailContent.BodyColumns;
     48 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
     49 import com.android.emailcommon.provider.EmailContent.Message;
     50 import com.android.emailcommon.provider.EmailContent.MessageColumns;
     51 import com.android.emailcommon.provider.EmailContent.PolicyColumns;
     52 import com.android.emailcommon.provider.HostAuth;
     53 import com.android.emailcommon.provider.Mailbox;
     54 import com.android.emailcommon.provider.Policy;
     55 import com.android.emailcommon.utility.TextUtilities;
     56 import com.android.emailcommon.utility.Utility;
     57 
     58 import java.io.File;
     59 import java.io.IOException;
     60 import java.util.ArrayList;
     61 
     62 /**
     63  * Tests of the Email provider.
     64  *
     65  * You can run this entire test case with:
     66  *   runtest -c com.android.email.provider.ProviderTests email
     67  *
     68  * TODO: Add tests for cursor notification mechanism.  (setNotificationUri and notifyChange)
     69  * We can't test the entire notification mechanism with a mock content resolver, because which URI
     70  * to notify when notifyChange() is called is in the actual content resolver.
     71  * Implementing the same mechanism in a mock one is pointless.  Instead what we could do is check
     72  * what notification URI each cursor has, and with which URI is notified when
     73  * inserting/updating/deleting.  (The former require a new method from AbstractCursor)
     74  */
     75 @LargeTest
     76 public class ProviderTests extends ProviderTestCase2<EmailProvider> {
     77 
     78     private EmailProvider mProvider;
     79     private Context mMockContext;
     80 
     81     public ProviderTests() {
     82         super(EmailProvider.class, EmailContent.AUTHORITY);
     83     }
     84 
     85     // TODO: move this out to a common place. There are other places that have similar mocks.
     86     /**
     87      * Private context wrapper used to add back getPackageName() for these tests.
     88      */
     89     private static class MockContext2 extends ContextWrapper {
     90 
     91         private final Context mRealContext;
     92 
     93         public MockContext2(Context mockContext, Context realContext) {
     94             super(mockContext);
     95             mRealContext = realContext;
     96         }
     97 
     98         @Override
     99         public Context getApplicationContext() {
    100             return this;
    101         }
    102 
    103         @Override
    104         public String getPackageName() {
    105             return mRealContext.getPackageName();
    106         }
    107 
    108         @Override
    109         public Object getSystemService(String name) {
    110             return mRealContext.getSystemService(name);
    111         }
    112     }
    113 
    114     private static final AttachmentService MOCK_ATTACHMENT_SERVICE = new AttachmentService() {
    115         @Override
    116         public void attachmentChanged(Context context, long id, int flags) {
    117             // Noop. Don't download attachments.
    118         }
    119     };
    120 
    121     @Override
    122     public void setUp() throws Exception {
    123         super.setUp();
    124         mMockContext = new MockContext2(getMockContext(), getContext());
    125         mProvider = getProvider();
    126         mProvider.injectAttachmentService(MOCK_ATTACHMENT_SERVICE);
    127         // Invalidate all caches, since we reset the database for each test
    128         ContentCache.invalidateAllCaches();
    129     }
    130 
    131     @Override
    132     public void tearDown() throws Exception {
    133         super.tearDown();
    134         mProvider.injectAttachmentService(null);
    135     }
    136 
    137     /**
    138      * TODO: Database upgrade tests
    139      */
    140 
    141     //////////////////////////////////////////////////////////
    142     ////// Utility methods
    143     //////////////////////////////////////////////////////////
    144 
    145     /** Sets the message count of all mailboxes to {@code -1}. */
    146     private void setMinusOneToMessageCounts() {
    147         ContentValues values = new ContentValues();
    148         values.put(MailboxColumns.MESSAGE_COUNT, -1);
    149 
    150         // EmailProvider.update() doesn't allow updating messageCount, so directly use the DB.
    151         SQLiteDatabase db = getProvider().getDatabase(mMockContext);
    152         db.update(Mailbox.TABLE_NAME, values, null, null);
    153     }
    154 
    155     /** Returns the number of messages in a mailbox. */
    156     private int getMessageCount(long mailboxId) {
    157         return Utility.getFirstRowInt(mMockContext,
    158                 ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId),
    159                 new String[] {MailboxColumns.MESSAGE_COUNT}, null, null, null, 0);
    160     }
    161 
    162     /** Creates a new message. */
    163     private static Message createMessage(Context c, Mailbox b, boolean starred, boolean read,
    164             int flagLoaded) {
    165         Message message = ProviderTestUtils.setupMessage(
    166                 "1", b.mAccountKey, b.mId, true, false, c, starred, read);
    167         message.mFlagLoaded = flagLoaded;
    168         message.save(c);
    169         return message;
    170     }
    171 
    172     //////////////////////////////////////////////////////////
    173     ////// The tests
    174     //////////////////////////////////////////////////////////
    175 
    176     /**
    177      * Test simple account save/retrieve
    178      */
    179     @SmallTest
    180     public void testAccountSave() {
    181         Account account1 = ProviderTestUtils.setupAccount("account-save", true, mMockContext);
    182         long account1Id = account1.mId;
    183 
    184         Account account2 = Account.restoreAccountWithId(mMockContext, account1Id);
    185 
    186         ProviderTestUtils.assertAccountEqual("testAccountSave", account1, account2);
    187     }
    188 
    189     /**
    190      * Test simple account save/retrieve with predefined hostauth records
    191      */
    192     @SmallTest
    193     public void testAccountSaveHostAuth() {
    194         Account account1 = ProviderTestUtils.setupAccount("account-hostauth", false, mMockContext);
    195         // add hostauth data, which should be saved the first time
    196         account1.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-hostauth-recv", -1, false,
    197                 mMockContext);
    198         account1.mHostAuthSend = ProviderTestUtils.setupHostAuth("account-hostauth-send", -1, false,
    199                 mMockContext);
    200         account1.save(mMockContext);
    201         long account1Id = account1.mId;
    202 
    203         // Confirm account reads back correctly
    204         Account account1get = Account.restoreAccountWithId(mMockContext, account1Id);
    205         ProviderTestUtils.assertAccountEqual("testAccountSave", account1, account1get);
    206 
    207         // Confirm hostauth fields can be accessed & read back correctly
    208         HostAuth hostAuth1get = HostAuth.restoreHostAuthWithId(mMockContext,
    209                 account1get.mHostAuthKeyRecv);
    210         ProviderTestUtils.assertHostAuthEqual("testAccountSaveHostAuth-recv",
    211                 account1.mHostAuthRecv, hostAuth1get);
    212         HostAuth hostAuth2get = HostAuth.restoreHostAuthWithId(mMockContext,
    213                 account1get.mHostAuthKeySend);
    214         ProviderTestUtils.assertHostAuthEqual("testAccountSaveHostAuth-send",
    215                 account1.mHostAuthSend, hostAuth2get);
    216     }
    217 
    218     public void testAccountGetHostAuthSend() {
    219         Account account = ProviderTestUtils.setupAccount("account-hostauth", false, mMockContext);
    220         account.mHostAuthSend = ProviderTestUtils.setupHostAuth("account-hostauth-send", -1, false,
    221                 mMockContext);
    222         account.save(mMockContext);
    223         HostAuth authGet;
    224         HostAuth authTest;
    225 
    226         authTest = account.mHostAuthSend;
    227         assertNotNull(authTest);
    228         assertTrue(account.mHostAuthKeySend != 0);
    229 
    230         // HostAuth is not changed
    231         authGet = account.getOrCreateHostAuthSend(mMockContext);
    232         assertTrue(authGet == authTest); // return the same object
    233 
    234         // New HostAuth; based upon mHostAuthKeyRecv
    235         authTest = HostAuth.restoreHostAuthWithId(mMockContext,
    236                 account.mHostAuthKeySend);
    237         account.mHostAuthSend = null;
    238         authGet = account.getOrCreateHostAuthSend(mMockContext);
    239         assertNotNull(authGet);
    240         assertNotNull(account.mHostAuthSend);
    241         ProviderTestUtils.assertHostAuthEqual("testAccountGetHostAuthSend-1", authTest, authGet);
    242 
    243         // New HostAuth; completely empty
    244         authTest = new HostAuth();
    245         account.mHostAuthSend = null;
    246         account.mHostAuthKeySend = 0;
    247         authGet = account.getOrCreateHostAuthSend(mMockContext);
    248         assertNotNull(authGet);
    249         assertNotNull(account.mHostAuthSend);
    250         ProviderTestUtils.assertHostAuthEqual("testAccountGetHostAuthSendv-2", authTest, authGet);
    251     }
    252 
    253     public void testAccountGetHostAuthRecv() {
    254         Account account = ProviderTestUtils.setupAccount("account-hostauth", false, mMockContext);
    255         account.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-hostauth-recv", -1, false,
    256                 mMockContext);
    257         account.save(mMockContext);
    258         HostAuth authGet;
    259         HostAuth authTest;
    260 
    261         authTest = account.mHostAuthRecv;
    262         assertNotNull(authTest);
    263         assertTrue(account.mHostAuthKeyRecv != 0);
    264 
    265         // HostAuth is not changed
    266         authGet = account.getOrCreateHostAuthRecv(mMockContext);
    267         assertTrue(authGet == authTest); // return the same object
    268 
    269         // New HostAuth; based upon mHostAuthKeyRecv
    270         authTest = HostAuth.restoreHostAuthWithId(mMockContext,
    271                 account.mHostAuthKeyRecv);
    272         account.mHostAuthRecv = null;
    273         authGet = account.getOrCreateHostAuthRecv(mMockContext);
    274         assertNotNull(authGet);
    275         assertNotNull(account.mHostAuthRecv);
    276         ProviderTestUtils.assertHostAuthEqual("testAccountGetHostAuthRecv-1", authTest, authGet);
    277 
    278         // New HostAuth; completely empty
    279         authTest = new HostAuth();
    280         account.mHostAuthRecv = null;
    281         account.mHostAuthKeyRecv = 0;
    282         authGet = account.getOrCreateHostAuthRecv(mMockContext);
    283         assertNotNull(authGet);
    284         assertNotNull(account.mHostAuthRecv);
    285         ProviderTestUtils.assertHostAuthEqual("testAccountGetHostAuthRecv-2", authTest, authGet);
    286     }
    287 
    288     /**
    289      * Simple test of account parceling.  The rather torturous path is to ensure that the
    290      * account is really flattened all the way down to a parcel and back.
    291      */
    292     public void testAccountParcel() {
    293         Account account1 = ProviderTestUtils.setupAccount("parcel", false, mMockContext);
    294         Bundle b = new Bundle();
    295         b.putParcelable("account", account1);
    296         Parcel p = Parcel.obtain();
    297         b.writeToParcel(p, 0);
    298         p.setDataPosition(0);       // rewind it for reading
    299         Bundle b2 = new Bundle(Account.class.getClassLoader());
    300         b2.readFromParcel(p);
    301         Account account2 = (Account) b2.getParcelable("account");
    302         p.recycle();
    303 
    304         ProviderTestUtils.assertAccountEqual("testAccountParcel", account1, account2);
    305     }
    306 
    307     /**
    308      * Test for {@link Account#getShortcutSafeUri()} and
    309      * {@link Account#getAccountIdFromShortcutSafeUri}.
    310      */
    311     public void testAccountShortcutSafeUri() {
    312         final Account account1 = ProviderTestUtils.setupAccount("account-1", true, mMockContext);
    313         final Account account2 = ProviderTestUtils.setupAccount("account-2", true, mMockContext);
    314         final long account1Id = account1.mId;
    315         final long account2Id = account2.mId;
    316 
    317         final Uri uri1 = account1.getShortcutSafeUri();
    318         final Uri uri2 = account2.getShortcutSafeUri();
    319 
    320         // Check the path part of the URIs.
    321         MoreAsserts.assertEquals(new String[] {"account", account1.mCompatibilityUuid},
    322                 uri1.getPathSegments().toArray());
    323         MoreAsserts.assertEquals(new String[] {"account", account2.mCompatibilityUuid},
    324                 uri2.getPathSegments().toArray());
    325 
    326         assertEquals(account1Id, Account.getAccountIdFromShortcutSafeUri(mMockContext, uri1));
    327         assertEquals(account2Id, Account.getAccountIdFromShortcutSafeUri(mMockContext, uri2));
    328 
    329         // Test for the Eclair(2.0-2.1) style URI.
    330         assertEquals(account1Id, Account.getAccountIdFromShortcutSafeUri(mMockContext,
    331                 getEclairStyleShortcutUri(account1)));
    332         assertEquals(account2Id, Account.getAccountIdFromShortcutSafeUri(mMockContext,
    333                 getEclairStyleShortcutUri(account2)));
    334     }
    335 
    336     private static Uri getEclairStyleShortcutUri(Account account) {
    337         // We used _id instead of UUID only on Eclair(2.0-2.1).
    338         return Account.CONTENT_URI.buildUpon().appendEncodedPath("" + account.mId).build();
    339     }
    340 
    341     public void testGetProtocol() {
    342         Account account1 = ProviderTestUtils.setupAccount("account-hostauth", false, mMockContext);
    343         // add hostauth data, with protocol
    344         account1.mHostAuthRecv = ProviderTestUtils.setupHostAuth("eas", "account-hostauth-recv",
    345                 false, mMockContext);
    346         // Note that getProtocol uses the receive host auth, so the protocol here shouldn't matter
    347         // to the test result
    348         account1.mHostAuthSend = ProviderTestUtils.setupHostAuth("foo", "account-hostauth-send",
    349                 false, mMockContext);
    350         account1.save(mMockContext);
    351         assertEquals("eas", Account.getProtocol(mMockContext, account1.mId));
    352         assertEquals("eas", account1.getProtocol(mMockContext));
    353         Account account2 = ProviderTestUtils.setupAccount("account-nohostauth", false,
    354                 mMockContext);
    355         account2.save(mMockContext);
    356         // Make sure that we return null when there's no host auth
    357         assertNull(Account.getProtocol(mMockContext, account2.mId));
    358         assertNull(account2.getProtocol(mMockContext));
    359         // And when there's no account
    360         assertNull(Account.getProtocol(mMockContext, 0));
    361     }
    362 
    363     public void testAccountIsValidId() {
    364         final Account account1 = ProviderTestUtils.setupAccount("account-1", true, mMockContext);
    365         final Account account2 = ProviderTestUtils.setupAccount("account-2", true, mMockContext);
    366 
    367         assertTrue(Account.isValidId(mMockContext, account1.mId));
    368         assertTrue(Account.isValidId(mMockContext, account2.mId));
    369 
    370         assertFalse(Account.isValidId(mMockContext, 1234567)); // Some random ID
    371         assertFalse(Account.isValidId(mMockContext, -1));
    372         assertFalse(Account.isValidId(mMockContext, -500));
    373     }
    374 
    375     private final static String[] MAILBOX_UNREAD_COUNT_PROJECTION = new String [] {
    376         MailboxColumns.UNREAD_COUNT
    377     };
    378     private final static int MAILBOX_UNREAD_COUNT_COLMUN = 0;
    379 
    380     /**
    381      * Get the value of the unread count in the mailbox of the account.
    382      * This can be different from the actual number of unread messages in that mailbox.
    383      */
    384     private int getUnreadCount(long mailboxId) {
    385         String text = null;
    386         Cursor c = null;
    387         try {
    388             c = mMockContext.getContentResolver().query(
    389                     Mailbox.CONTENT_URI,
    390                     MAILBOX_UNREAD_COUNT_PROJECTION,
    391                     EmailContent.RECORD_ID + "=?",
    392                     new String[] { String.valueOf(mailboxId) },
    393                     null);
    394             c.moveToFirst();
    395             text = c.getString(MAILBOX_UNREAD_COUNT_COLMUN);
    396         } finally {
    397             c.close();
    398         }
    399         return Integer.valueOf(text);
    400     }
    401 
    402     private static String[] expectedAttachmentNames =
    403         new String[] {"attachment1.doc", "attachment2.xls", "attachment3"};
    404     // The lengths need to be kept in ascending order
    405     private static long[] expectedAttachmentSizes = new long[] {31415L, 97701L, 151213L};
    406 
    407     /*
    408      * Returns null if the message has no body.
    409      */
    410     private Body loadBodyForMessageId(long messageId) {
    411         Cursor c = null;
    412         try {
    413             c = mMockContext.getContentResolver().query(
    414                     EmailContent.Body.CONTENT_URI,
    415                     EmailContent.Body.CONTENT_PROJECTION,
    416                     EmailContent.Body.MESSAGE_KEY + "=?",
    417                     new String[] {String.valueOf(messageId)},
    418                     null);
    419             int numBodies = c.getCount();
    420             assertTrue("at most one body", numBodies < 2);
    421             return c.moveToFirst() ? EmailContent.getContent(c, Body.class) : null;
    422         } finally {
    423             c.close();
    424         }
    425     }
    426 
    427     /**
    428      * Test simple message save/retrieve
    429      *
    430      * TODO: serverId vs. serverIntId
    431      */
    432     @MediumTest
    433     public void testMessageSave() {
    434         Account account1 = ProviderTestUtils.setupAccount("message-save", true, mMockContext);
    435         long account1Id = account1.mId;
    436         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
    437         long box1Id = box1.mId;
    438 
    439         // Test a simple message (saved with no body)
    440         Message message1 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, false,
    441                 true, mMockContext);
    442         long message1Id = message1.mId;
    443         Message message1get = EmailContent.Message.restoreMessageWithId(mMockContext, message1Id);
    444         ProviderTestUtils.assertMessageEqual("testMessageSave", message1, message1get);
    445 
    446         // Test a message saved with a body
    447         // Note that it will read back w/o the text & html so we must extract those
    448         Message message2 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, true,
    449                 true, mMockContext);
    450         long message2Id = message2.mId;
    451         String text2 = message2.mText;
    452         String html2 = message2.mHtml;
    453         String textReply2 = message2.mTextReply;
    454         String htmlReply2 = message2.mHtmlReply;
    455         long sourceKey2 = message2.mSourceKey;
    456         String introText2 = message2.mIntroText;
    457         message2.mText = null;
    458         message2.mHtml = null;
    459         message2.mTextReply = null;
    460         message2.mHtmlReply = null;
    461         message2.mSourceKey = 0;
    462         message2.mIntroText = null;
    463         Message message2get = EmailContent.Message.restoreMessageWithId(mMockContext, message2Id);
    464         ProviderTestUtils.assertMessageEqual("testMessageSave", message2, message2get);
    465 
    466         // Now see if there's a body saved with the right stuff
    467         Body body2 = loadBodyForMessageId(message2Id);
    468         assertEquals("body text", text2, body2.mTextContent);
    469         assertEquals("body html", html2, body2.mHtmlContent);
    470         assertEquals("reply text", textReply2, body2.mTextReply);
    471         assertEquals("reply html", htmlReply2, body2.mHtmlReply);
    472         assertEquals("source key", sourceKey2, body2.mSourceKey);
    473         assertEquals("intro text", introText2, body2.mIntroText);
    474     }
    475 
    476     @MediumTest
    477     public void testMessageWithAttachment() {
    478         Account account1 = ProviderTestUtils.setupAccount("message-save", true, mMockContext);
    479         long account1Id = account1.mId;
    480         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
    481         long box1Id = box1.mId;
    482 
    483         // Message with attachments and body
    484         Message message3 = ProviderTestUtils.setupMessage("message3", account1Id, box1Id, true,
    485                 false, mMockContext);
    486         ArrayList<Attachment> atts = new ArrayList<Attachment>();
    487         for (int i = 0; i < 3; i++) {
    488             atts.add(ProviderTestUtils.setupAttachment(
    489                     -1, expectedAttachmentNames[i], expectedAttachmentSizes[i],
    490                     false, mMockContext));
    491         }
    492         message3.mAttachments = atts;
    493         message3.save(mMockContext);
    494         long message3Id = message3.mId;
    495 
    496         // Now check the attachments; there should be three and they should match name and size
    497         Cursor c = null;
    498         try {
    499             // Note that there is NO guarantee of the order of returned records in the general case,
    500             // so we specifically ask for ordering by size.  The expectedAttachmentSizes array must
    501             // be kept sorted by size (ascending) for this test to work properly
    502             c = mMockContext.getContentResolver().query(
    503                     Attachment.CONTENT_URI,
    504                     Attachment.CONTENT_PROJECTION,
    505                     Attachment.MESSAGE_KEY + "=?",
    506                     new String[] {
    507                             String.valueOf(message3Id)
    508                     },
    509                     Attachment.SIZE);
    510             int numAtts = c.getCount();
    511             assertEquals(3, numAtts);
    512             int i = 0;
    513             while (c.moveToNext()) {
    514                 Attachment actual = EmailContent.getContent(c, Attachment.class);
    515                 ProviderTestUtils.assertAttachmentEqual("save-message3", atts.get(i), actual);
    516                 i++;
    517             }
    518         } finally {
    519             c.close();
    520         }
    521     }
    522 
    523 
    524     @MediumTest
    525     public void testMessageSaveWithJustAttachments() {
    526         Account account1 = ProviderTestUtils.setupAccount("message-save", true, mMockContext);
    527         long account1Id = account1.mId;
    528         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
    529         long box1Id = box1.mId;
    530         Cursor c = null;
    531 
    532         // Message with attachments but no body
    533         Message message4 = ProviderTestUtils.setupMessage("message4", account1Id, box1Id, false,
    534                 false, mMockContext);
    535         ArrayList<Attachment> atts = new ArrayList<Attachment>();
    536         for (int i = 0; i < 3; i++) {
    537             atts.add(ProviderTestUtils.setupAttachment(
    538                     -1, expectedAttachmentNames[i], expectedAttachmentSizes[i],
    539                     false, mMockContext));
    540         }
    541         message4.mAttachments = atts;
    542         message4.save(mMockContext);
    543         long message4Id = message4.mId;
    544 
    545         // Now check the attachments; there should be three and they should match name and size
    546         c = null;
    547 
    548         try {
    549             // Note that there is NO guarantee of the order of returned records in the general case,
    550             // so we specifically ask for ordering by size.  The expectedAttachmentSizes array must
    551             // be kept sorted by size (ascending) for this test to work properly
    552             c = mMockContext.getContentResolver().query(
    553                     Attachment.CONTENT_URI,
    554                     Attachment.CONTENT_PROJECTION,
    555                     Attachment.MESSAGE_KEY + "=?",
    556                     new String[] {
    557                             String.valueOf(message4Id)
    558                     },
    559                     Attachment.SIZE);
    560             int numAtts = c.getCount();
    561             assertEquals(3, numAtts);
    562             int i = 0;
    563             while (c.moveToNext()) {
    564                 Attachment actual = EmailContent.getContent(c, Attachment.class);
    565                 ProviderTestUtils.assertAttachmentEqual("save-message4", atts.get(i), actual);
    566                 i++;
    567             }
    568         } finally {
    569             c.close();
    570         }
    571 
    572         // test EmailContent.restoreAttachmentsWitdMessageId()
    573         Attachment[] attachments =
    574             Attachment.restoreAttachmentsWithMessageId(mMockContext, message4Id);
    575         int size = attachments.length;
    576         assertEquals(3, size);
    577         for (int i = 0; i < size; ++i) {
    578             ProviderTestUtils.assertAttachmentEqual("save-message4", atts.get(i), attachments[i]);
    579         }
    580     }
    581 
    582     /**
    583      * Test that saving a message creates the proper snippet for that message
    584      */
    585     public void testMessageSaveAddsSnippet() {
    586         Account account = ProviderTestUtils.setupAccount("message-snippet", true, mMockContext);
    587         Mailbox box = ProviderTestUtils.setupMailbox("box1", account.mId, true, mMockContext);
    588 
    589         // Create a message without a body, unsaved
    590         Message message = ProviderTestUtils.setupMessage("message", account.mId, box.mId, false,
    591                 false, mMockContext);
    592         message.mText = "This is some text";
    593         message.mHtml = "<html>This is some text</html>";
    594         message.save(mMockContext);
    595         Message restoredMessage = Message.restoreMessageWithId(mMockContext, message.mId);
    596         // We should have the plain text as the snippet
    597         assertEquals(restoredMessage.mSnippet,
    598                 TextUtilities.makeSnippetFromPlainText(message.mText));
    599 
    600         // Start again
    601         message = ProviderTestUtils.setupMessage("message", account.mId, box.mId, false,
    602                 false, mMockContext);
    603         message.mText = null;
    604         message.mHtml = "<html>This is some text</html>";
    605         message.save(mMockContext);
    606         restoredMessage = Message.restoreMessageWithId(mMockContext, message.mId);
    607         // We should have the plain text as the snippet
    608         assertEquals(restoredMessage.mSnippet,
    609                 TextUtilities.makeSnippetFromHtmlText(message.mHtml));
    610     }
    611 
    612     /**
    613      * TODO: update account
    614      */
    615 
    616     /**
    617      * TODO: update mailbox
    618      */
    619 
    620     /**
    621      * TODO: update message
    622      */
    623 
    624     /**
    625      * Test delete account
    626      * TODO: hostauth
    627      */
    628     public void testAccountDelete() {
    629         Account account1 = ProviderTestUtils.setupAccount("account-delete-1", true, mMockContext);
    630         long account1Id = account1.mId;
    631         Account account2 = ProviderTestUtils.setupAccount("account-delete-2", true, mMockContext);
    632         long account2Id = account2.mId;
    633 
    634         // make sure there are two accounts
    635         int numBoxes = EmailContent.count(mMockContext, Account.CONTENT_URI, null, null);
    636         assertEquals(2, numBoxes);
    637 
    638         // now delete one of them
    639         Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, account1Id);
    640         mMockContext.getContentResolver().delete(uri, null, null);
    641 
    642         // make sure there's only one account now
    643         numBoxes = EmailContent.count(mMockContext, Account.CONTENT_URI, null, null);
    644         assertEquals(1, numBoxes);
    645 
    646         // now delete the other one
    647         uri = ContentUris.withAppendedId(Account.CONTENT_URI, account2Id);
    648         mMockContext.getContentResolver().delete(uri, null, null);
    649 
    650         // make sure there are no accounts now
    651         numBoxes = EmailContent.count(mMockContext, Account.CONTENT_URI, null, null);
    652         assertEquals(0, numBoxes);
    653     }
    654 
    655     /**
    656      * Test for Body.lookupBodyIdWithMessageId()
    657      * Verifies that:
    658      * - for a message without body, -1 is returned.
    659      * - for a mesage with body, the id matches the one from loadBodyForMessageId.
    660      */
    661     public void testLookupBodyIdWithMessageId() {
    662         final ContentResolver resolver = mMockContext.getContentResolver();
    663         Account account1 = ProviderTestUtils.setupAccount("orphaned body", true, mMockContext);
    664         long account1Id = account1.mId;
    665         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
    666         long box1Id = box1.mId;
    667 
    668         // 1. create message with no body, check that returned bodyId is -1
    669         Message message1 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, false,
    670                 true, mMockContext);
    671         long message1Id = message1.mId;
    672         long bodyId1 = Body.lookupBodyIdWithMessageId(mMockContext, message1Id);
    673         assertEquals(bodyId1, -1);
    674 
    675         // 2. create message with body, check that returned bodyId is correct
    676         Message message2 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, true,
    677                 true, mMockContext);
    678         long message2Id = message2.mId;
    679         long bodyId2 = Body.lookupBodyIdWithMessageId(mMockContext, message2Id);
    680         Body body = loadBodyForMessageId(message2Id);
    681         assertNotNull(body);
    682         assertEquals(body.mId, bodyId2);
    683     }
    684 
    685     /**
    686      * Test for Body.updateBodyWithMessageId().
    687      * 1. - create message without body,
    688      *    - update its body (set TEXT_CONTENT)
    689      *    - check correct updated body is read back
    690      *
    691      * 2. - create message with body,
    692      *    - update body (set TEXT_CONTENT)
    693      *    - check correct updated body is read back
    694      */
    695     public void testUpdateBodyWithMessageId() {
    696         Account account1 = ProviderTestUtils.setupAccount("orphaned body", true, mMockContext);
    697         long account1Id = account1.mId;
    698         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
    699         long box1Id = box1.mId;
    700 
    701         final String textContent = "foobar some odd text";
    702         final String htmlContent = "and some html";
    703         final String textReply = "plain text reply";
    704         final String htmlReply = "or the html reply";
    705         final String introText = "fred wrote:";
    706 
    707         ContentValues values = new ContentValues();
    708         values.put(BodyColumns.TEXT_CONTENT, textContent);
    709         values.put(BodyColumns.HTML_CONTENT, htmlContent);
    710         values.put(BodyColumns.TEXT_REPLY, textReply);
    711         values.put(BodyColumns.HTML_REPLY, htmlReply);
    712         values.put(BodyColumns.SOURCE_MESSAGE_KEY, 17);
    713         values.put(BodyColumns.INTRO_TEXT, introText);
    714 
    715         // 1
    716         Message message1 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, false,
    717                 true, mMockContext);
    718         long message1Id = message1.mId;
    719         Body body1 = loadBodyForMessageId(message1Id);
    720         assertNull(body1);
    721         Body.updateBodyWithMessageId(mMockContext, message1Id, values);
    722         body1 = loadBodyForMessageId(message1Id);
    723         assertNotNull(body1);
    724         assertEquals(body1.mTextContent, textContent);
    725         assertEquals(body1.mHtmlContent, htmlContent);
    726         assertEquals(body1.mTextReply, textReply);
    727         assertEquals(body1.mHtmlReply, htmlReply);
    728         assertEquals(body1.mSourceKey, 17);
    729         assertEquals(body1.mIntroText, introText);
    730 
    731         // 2
    732         Message message2 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, true,
    733                 true, mMockContext);
    734         long message2Id = message2.mId;
    735         Body body2 = loadBodyForMessageId(message2Id);
    736         assertNotNull(body2);
    737         assertTrue(!body2.mTextContent.equals(textContent));
    738         Body.updateBodyWithMessageId(mMockContext, message2Id, values);
    739         body2 = loadBodyForMessageId(message1Id);
    740         assertNotNull(body2);
    741         assertEquals(body2.mTextContent, textContent);
    742         assertEquals(body2.mHtmlContent, htmlContent);
    743         assertEquals(body2.mTextReply, textReply);
    744         assertEquals(body2.mHtmlReply, htmlReply);
    745         assertEquals(body2.mSourceKey, 17);
    746         assertEquals(body2.mIntroText, introText);
    747     }
    748 
    749     /**
    750      * Test body retrieve methods
    751      */
    752     public void testBodyRetrieve() {
    753         // No account needed
    754         // No mailbox needed
    755         Message message1 = ProviderTestUtils.setupMessage("bodyretrieve", 1, 1, true,
    756                 true, mMockContext);
    757         long messageId = message1.mId;
    758 
    759         assertEquals(message1.mText,
    760                 Body.restoreBodyTextWithMessageId(mMockContext, messageId));
    761         assertEquals(message1.mHtml,
    762                 Body.restoreBodyHtmlWithMessageId(mMockContext, messageId));
    763         assertEquals(message1.mTextReply,
    764                 Body.restoreReplyTextWithMessageId(mMockContext, messageId));
    765         assertEquals(message1.mHtmlReply,
    766                 Body.restoreReplyHtmlWithMessageId(mMockContext, messageId));
    767         assertEquals(message1.mIntroText,
    768                 Body.restoreIntroTextWithMessageId(mMockContext, messageId));
    769         assertEquals(message1.mSourceKey,
    770                 Body.restoreBodySourceKey(mMockContext, messageId));
    771     }
    772 
    773     /**
    774      * Test delete body.
    775      * 1. create message without body (message id 1)
    776      * 2. create message with body (message id 2. The body has _id 1 and messageKey 2).
    777      * 3. delete first message.
    778      * 4. verify that body for message 2 has not been deleted.
    779      * 5. delete message 2, verify body is deleted.
    780      */
    781     public void testDeleteBody() {
    782         final ContentResolver resolver = mMockContext.getContentResolver();
    783 
    784         // Create account and mailboxes
    785         Account account1 = ProviderTestUtils.setupAccount("orphaned body", true, mMockContext);
    786         long account1Id = account1.mId;
    787         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
    788         long box1Id = box1.mId;
    789 
    790         // 1. create message without body
    791         Message message1 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, false,
    792                 true, mMockContext);
    793         long message1Id = message1.mId;
    794 
    795         // 2. create message with body
    796         Message message2 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, true,
    797                 true, mMockContext);
    798         long message2Id = message2.mId;
    799         // verify body is there
    800         assertNotNull(loadBodyForMessageId(message2Id));
    801 
    802         // 3. delete first message
    803         resolver.delete(ContentUris.withAppendedId(Message.CONTENT_URI, message1Id), null, null);
    804 
    805         // 4. verify body for second message wasn't deleted
    806         assertNotNull(loadBodyForMessageId(message2Id));
    807 
    808         // 5. delete second message, check its body is deleted
    809         resolver.delete(ContentUris.withAppendedId(Message.CONTENT_URI, message2Id), null, null);
    810         assertNull(loadBodyForMessageId(message2Id));
    811     }
    812 
    813     /**
    814      * Test delete orphan bodies.
    815      * 1. create message without body (message id 1)
    816      * 2. create message with body (message id 2. Body has _id 1 and messageKey 2).
    817      * 3. delete first message.
    818      * 4. delete some other mailbox -- this triggers delete orphan bodies.
    819      * 5. verify that body for message 2 has not been deleted.
    820      */
    821     public void testDeleteOrphanBodies() {
    822         final ContentResolver resolver = mMockContext.getContentResolver();
    823 
    824         // Create account and two mailboxes
    825         Account account1 = ProviderTestUtils.setupAccount("orphaned body", true, mMockContext);
    826         long account1Id = account1.mId;
    827         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
    828         long box1Id = box1.mId;
    829         Mailbox box2 = ProviderTestUtils.setupMailbox("box2", account1Id, true, mMockContext);
    830         long box2Id = box2.mId;
    831 
    832         // 1. create message without body
    833         Message message1 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, false,
    834                 true, mMockContext);
    835         long message1Id = message1.mId;
    836 
    837         // 2. create message with body
    838         Message message2 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, true,
    839                 true, mMockContext);
    840         long message2Id = message2.mId;
    841         //verify body is there
    842         assertNotNull(loadBodyForMessageId(message2Id));
    843 
    844         // 3. delete first message
    845         resolver.delete(ContentUris.withAppendedId(Message.CONTENT_URI, message1Id), null, null);
    846 
    847         // 4. delete some mailbox (because it triggers "delete orphan bodies")
    848         resolver.delete(ContentUris.withAppendedId(Mailbox.CONTENT_URI, box2Id), null, null);
    849 
    850         // 5. verify body for second message wasn't deleted during "delete orphan bodies"
    851         assertNotNull(loadBodyForMessageId(message2Id));
    852     }
    853 
    854     /**
    855      * Note that we can't use EmailContent.count() here because it uses a projection including
    856      * count(*), and count(*) is incompatible with a LIMIT (i.e. the limit would be applied to the
    857      * single column returned with count(*), rather than to the query itself)
    858      */
    859     private int count(Context context, Uri uri, String selection, String[] selectionArgs) {
    860         Cursor c = context.getContentResolver().query(uri, EmailContent.ID_PROJECTION, selection,
    861                 selectionArgs, null);
    862         try {
    863             return c.getCount();
    864         } finally {
    865             c.close();
    866         }
    867     }
    868 
    869     public void testMessageQueryWithLimit() {
    870         final Context context = mMockContext;
    871 
    872         // Create account and two mailboxes
    873         Account acct = ProviderTestUtils.setupAccount("orphaned body", true, context);
    874         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", acct.mId, true, context);
    875         Mailbox box2 = ProviderTestUtils.setupMailbox("box2", acct.mId, true, context);
    876 
    877         // Create 4 messages in box1
    878         ProviderTestUtils.setupMessage("message1", acct.mId, box1.mId, false, true, context);
    879         ProviderTestUtils.setupMessage("message2", acct.mId, box1.mId, false, true, context);
    880         ProviderTestUtils.setupMessage("message3", acct.mId, box1.mId, false, true, context);
    881         ProviderTestUtils.setupMessage("message4", acct.mId, box1.mId, false, true, context);
    882 
    883         // Create 4 messages in box2
    884         ProviderTestUtils.setupMessage("message1", acct.mId, box2.mId, false, true, context);
    885         ProviderTestUtils.setupMessage("message2", acct.mId, box2.mId, false, true, context);
    886         ProviderTestUtils.setupMessage("message3", acct.mId, box2.mId, false, true, context);
    887         ProviderTestUtils.setupMessage("message4", acct.mId, box2.mId, false, true, context);
    888 
    889         // Check normal case, special case (limit 1), and arbitrary limits
    890         assertEquals(8, count(mMockContext, Message.CONTENT_URI, null, null));
    891         assertEquals(1, count(mMockContext, EmailContent.uriWithLimit(Message.CONTENT_URI, 1),
    892                 null, null));
    893         assertEquals(3, count(mMockContext, EmailContent.uriWithLimit(Message.CONTENT_URI, 3),
    894                 null, null));
    895         assertEquals(8, count(mMockContext, EmailContent.uriWithLimit(Message.CONTENT_URI, 100),
    896                 null, null));
    897 
    898         // Check that it works with selection/selection args
    899         String[] args = new String[] {Long.toString(box1.mId)};
    900         assertEquals(4, count(mMockContext, Message.CONTENT_URI,
    901                 MessageColumns.MAILBOX_KEY + "=?", args));
    902         assertEquals(1, count(mMockContext,
    903                 EmailContent.uriWithLimit(Message.CONTENT_URI, 1),
    904                 MessageColumns.MAILBOX_KEY + "=?", args));
    905     }
    906 
    907     /**
    908      * Test delete orphan messages
    909      * 1. create message without body (message id 1)
    910      * 2. create message with body (message id 2. Body has _id 1 and messageKey 2).
    911      * 3. delete first message.
    912      * 4. delete some other mailbox -- this triggers delete orphan bodies.
    913      * 5. verify that body for message 2 has not been deleted.
    914      */
    915      public void testDeleteOrphanMessages() {
    916         final ContentResolver resolver = mMockContext.getContentResolver();
    917         final Context context = mMockContext;
    918 
    919         // Create account and two mailboxes
    920         Account acct = ProviderTestUtils.setupAccount("orphaned body", true, context);
    921         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", acct.mId, true, context);
    922         Mailbox box2 = ProviderTestUtils.setupMailbox("box2", acct.mId, true, context);
    923 
    924         // Create 4 messages in box1
    925         Message msg1_1 =
    926             ProviderTestUtils.setupMessage("message1", acct.mId, box1.mId, false, true, context);
    927         Message msg1_2 =
    928             ProviderTestUtils.setupMessage("message2", acct.mId, box1.mId, false, true, context);
    929         Message msg1_3 =
    930             ProviderTestUtils.setupMessage("message3", acct.mId, box1.mId, false, true, context);
    931         Message msg1_4 =
    932             ProviderTestUtils.setupMessage("message4", acct.mId, box1.mId, false, true, context);
    933 
    934         // Create 4 messages in box2
    935         Message msg2_1 =
    936             ProviderTestUtils.setupMessage("message1", acct.mId, box2.mId, false, true, context);
    937         Message msg2_2 =
    938             ProviderTestUtils.setupMessage("message2", acct.mId, box2.mId, false, true, context);
    939         Message msg2_3 =
    940             ProviderTestUtils.setupMessage("message3", acct.mId, box2.mId, false, true, context);
    941         Message msg2_4 =
    942             ProviderTestUtils.setupMessage("message4", acct.mId, box2.mId, false, true, context);
    943 
    944         // Delete 2 from each mailbox
    945         resolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, msg1_1.mId),
    946                 null, null);
    947         resolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, msg1_2.mId),
    948                 null, null);
    949         resolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, msg2_1.mId),
    950                 null, null);
    951         resolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, msg2_2.mId),
    952                 null, null);
    953 
    954         // There should be 4 items in the deleted item table
    955         assertEquals(4, EmailContent.count(context, Message.DELETED_CONTENT_URI, null, null));
    956 
    957         // Update 2 from each mailbox
    958         ContentValues v = new ContentValues();
    959         v.put(MessageColumns.DISPLAY_NAME, "--updated--");
    960         resolver.update(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, msg1_3.mId),
    961                 v, null, null);
    962         resolver.update(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, msg1_4.mId),
    963                 v, null, null);
    964         resolver.update(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, msg2_3.mId),
    965                 v, null, null);
    966         resolver.update(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, msg2_4.mId),
    967                 v, null, null);
    968 
    969          // There should be 4 items in the updated item table
    970         assertEquals(4, EmailContent.count(context, Message.UPDATED_CONTENT_URI, null, null));
    971 
    972         // Manually add 2 messages from a "deleted" mailbox to deleted and updated tables
    973         // Use a value > 2 for the deleted box id
    974         long delBoxId = 10;
    975         // Create 4 messages in the "deleted" mailbox
    976         Message msgX_A =
    977             ProviderTestUtils.setupMessage("messageA", acct.mId, delBoxId, false, false, context);
    978         Message msgX_B =
    979             ProviderTestUtils.setupMessage("messageB", acct.mId, delBoxId, false, false, context);
    980         Message msgX_C =
    981             ProviderTestUtils.setupMessage("messageC", acct.mId, delBoxId, false, false, context);
    982         Message msgX_D =
    983             ProviderTestUtils.setupMessage("messageD", acct.mId, delBoxId, false, false, context);
    984 
    985         ContentValues cv;
    986         // We have to assign id's manually because there are no autoincrement id's for these tables
    987         // Start with an id that won't exist, since id's in these tables must be unique
    988         long msgId = 10;
    989         // It's illegal to manually insert these, so we need to catch the exception
    990         // NOTE: The insert succeeds, and then throws the exception
    991         try {
    992             cv = msgX_A.toContentValues();
    993             cv.put(EmailContent.RECORD_ID, msgId++);
    994             resolver.insert(Message.DELETED_CONTENT_URI, cv);
    995         } catch (IllegalArgumentException e) {
    996         }
    997         try {
    998             cv = msgX_B.toContentValues();
    999             cv.put(EmailContent.RECORD_ID, msgId++);
   1000             resolver.insert(Message.DELETED_CONTENT_URI, cv);
   1001         } catch (IllegalArgumentException e) {
   1002         }
   1003         try {
   1004             cv = msgX_C.toContentValues();
   1005             cv.put(EmailContent.RECORD_ID, msgId++);
   1006             resolver.insert(Message.UPDATED_CONTENT_URI, cv);
   1007         } catch (IllegalArgumentException e) {
   1008         }
   1009         try {
   1010             cv = msgX_D.toContentValues();
   1011             cv.put(EmailContent.RECORD_ID, msgId++);
   1012             resolver.insert(Message.UPDATED_CONTENT_URI, cv);
   1013         } catch (IllegalArgumentException e) {
   1014         }
   1015 
   1016         // There should be 6 items in the deleted and updated tables
   1017         assertEquals(6, EmailContent.count(context, Message.UPDATED_CONTENT_URI, null, null));
   1018         assertEquals(6, EmailContent.count(context, Message.DELETED_CONTENT_URI, null, null));
   1019 
   1020         // Delete the orphans
   1021         EmailProvider.deleteMessageOrphans(EmailProvider.getReadableDatabase(context),
   1022                 Message.DELETED_TABLE_NAME);
   1023         EmailProvider.deleteMessageOrphans(EmailProvider.getReadableDatabase(context),
   1024                 Message.UPDATED_TABLE_NAME);
   1025 
   1026         // There should now be 4 messages in each of the deleted and updated tables again
   1027         assertEquals(4, EmailContent.count(context, Message.UPDATED_CONTENT_URI, null, null));
   1028         assertEquals(4, EmailContent.count(context, Message.DELETED_CONTENT_URI, null, null));
   1029     }
   1030 
   1031     /**
   1032      * Test delete message
   1033      * TODO: body
   1034      * TODO: attachments
   1035      */
   1036     public void testMessageDelete() {
   1037         Account account1 = ProviderTestUtils.setupAccount("message-delete", true, mMockContext);
   1038         long account1Id = account1.mId;
   1039         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
   1040         long box1Id = box1.mId;
   1041         Message message1 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, false,
   1042                 true, mMockContext);
   1043         long message1Id = message1.mId;
   1044         Message message2 = ProviderTestUtils.setupMessage("message2", account1Id, box1Id, false,
   1045                 true, mMockContext);
   1046         long message2Id = message2.mId;
   1047 
   1048         String selection = EmailContent.MessageColumns.ACCOUNT_KEY + "=? AND " +
   1049                 EmailContent.MessageColumns.MAILBOX_KEY + "=?";
   1050         String[] selArgs = new String[] { String.valueOf(account1Id), String.valueOf(box1Id) };
   1051 
   1052         // make sure there are two messages
   1053         int numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, selection, selArgs);
   1054         assertEquals(2, numMessages);
   1055 
   1056         // now delete one of them
   1057         Uri uri = ContentUris.withAppendedId(Message.CONTENT_URI, message1Id);
   1058         mMockContext.getContentResolver().delete(uri, null, null);
   1059 
   1060         // make sure there's only one message now
   1061         numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, selection, selArgs);
   1062         assertEquals(1, numMessages);
   1063 
   1064         // now delete the other one
   1065         uri = ContentUris.withAppendedId(Message.CONTENT_URI, message2Id);
   1066         mMockContext.getContentResolver().delete(uri, null, null);
   1067 
   1068         // make sure there are no messages now
   1069         numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, selection, selArgs);
   1070         assertEquals(0, numMessages);
   1071     }
   1072 
   1073     /**
   1074      * Test delete synced message
   1075      * TODO: body
   1076      * TODO: attachments
   1077      */
   1078     public void testSyncedMessageDelete() {
   1079         Account account1 = ProviderTestUtils.setupAccount("synced-message-delete", true,
   1080                 mMockContext);
   1081         long account1Id = account1.mId;
   1082         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
   1083         long box1Id = box1.mId;
   1084         Message message1 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, false,
   1085                 true, mMockContext);
   1086         long message1Id = message1.mId;
   1087         Message message2 = ProviderTestUtils.setupMessage("message2", account1Id, box1Id, false,
   1088                 true, mMockContext);
   1089         long message2Id = message2.mId;
   1090 
   1091         String selection = EmailContent.MessageColumns.ACCOUNT_KEY + "=? AND "
   1092                 + EmailContent.MessageColumns.MAILBOX_KEY + "=?";
   1093         String[] selArgs = new String[] {
   1094             String.valueOf(account1Id), String.valueOf(box1Id)
   1095         };
   1096 
   1097         // make sure there are two messages
   1098         int numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, selection, selArgs);
   1099         assertEquals(2, numMessages);
   1100 
   1101         // make sure we start with no synced deletions
   1102         numMessages = EmailContent.count(mMockContext, Message.DELETED_CONTENT_URI, selection,
   1103                 selArgs);
   1104         assertEquals(0, numMessages);
   1105 
   1106         // now delete one of them SYNCED
   1107         Uri uri = ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, message1Id);
   1108         mMockContext.getContentResolver().delete(uri, null, null);
   1109 
   1110         // make sure there's only one message now
   1111         numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, selection, selArgs);
   1112         assertEquals(1, numMessages);
   1113 
   1114         // make sure there's one synced deletion now
   1115         numMessages = EmailContent.count(mMockContext, Message.DELETED_CONTENT_URI, selection,
   1116                 selArgs);
   1117         assertEquals(1, numMessages);
   1118 
   1119         // now delete the other one NOT SYNCED
   1120         uri = ContentUris.withAppendedId(Message.CONTENT_URI, message2Id);
   1121         mMockContext.getContentResolver().delete(uri, null, null);
   1122 
   1123         // make sure there are no messages now
   1124         numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, selection, selArgs);
   1125         assertEquals(0, numMessages);
   1126 
   1127         // make sure there's still one deletion now
   1128         numMessages = EmailContent.count(mMockContext, Message.DELETED_CONTENT_URI, selection,
   1129                 selArgs);
   1130         assertEquals(1, numMessages);
   1131     }
   1132 
   1133     /**
   1134      * Test message update
   1135      * TODO: body
   1136      * TODO: attachments
   1137      */
   1138     public void testMessageUpdate() {
   1139         Account account1 = ProviderTestUtils.setupAccount("message-update", true, mMockContext);
   1140         long account1Id = account1.mId;
   1141         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
   1142         long box1Id = box1.mId;
   1143         Message message1 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, false,
   1144                 true, mMockContext);
   1145         long message1Id = message1.mId;
   1146         Message message2 = ProviderTestUtils.setupMessage("message2", account1Id, box1Id, false,
   1147                 true, mMockContext);
   1148         long message2Id = message2.mId;
   1149         ContentResolver cr = mMockContext.getContentResolver();
   1150 
   1151         String selection = EmailContent.MessageColumns.ACCOUNT_KEY + "=? AND "
   1152                 + EmailContent.MessageColumns.MAILBOX_KEY + "=?";
   1153         String[] selArgs = new String[] {
   1154             String.valueOf(account1Id), String.valueOf(box1Id)
   1155         };
   1156 
   1157         // make sure there are two messages
   1158         int numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, selection, selArgs);
   1159         assertEquals(2, numMessages);
   1160 
   1161         // change the first one
   1162         Uri uri = ContentUris.withAppendedId(Message.CONTENT_URI, message1Id);
   1163         ContentValues cv = new ContentValues();
   1164         cv.put(MessageColumns.FROM_LIST, "from-list");
   1165         cr.update(uri, cv, null, null);
   1166 
   1167         // make sure there's no updated message
   1168         numMessages = EmailContent.count(mMockContext, Message.UPDATED_CONTENT_URI, selection,
   1169                 selArgs);
   1170         assertEquals(0, numMessages);
   1171 
   1172         // get the message back from the provider, make sure the change "stuck"
   1173         Message restoredMessage = Message.restoreMessageWithId(mMockContext, message1Id);
   1174         assertEquals("from-list", restoredMessage.mFrom);
   1175 
   1176         // change the second one
   1177         uri = ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, message2Id);
   1178         cv = new ContentValues();
   1179         cv.put(MessageColumns.FROM_LIST, "from-list");
   1180         cr.update(uri, cv, null, null);
   1181 
   1182         // make sure there's one updated message
   1183         numMessages = EmailContent.count(mMockContext, Message.UPDATED_CONTENT_URI, selection,
   1184                 selArgs);
   1185         assertEquals(1, numMessages);
   1186 
   1187         // get the message back from the provider, make sure the change "stuck",
   1188         // as before
   1189         restoredMessage = Message.restoreMessageWithId(mMockContext, message2Id);
   1190         assertEquals("from-list", restoredMessage.mFrom);
   1191 
   1192         // get the original message back from the provider
   1193         Cursor c = cr.query(Message.UPDATED_CONTENT_URI, Message.CONTENT_PROJECTION, null, null,
   1194                 null);
   1195         try {
   1196             assertTrue(c.moveToFirst());
   1197             Message originalMessage = EmailContent.getContent(c, Message.class);
   1198             // make sure this has the original value
   1199             assertEquals("from message2", originalMessage.mFrom);
   1200             // Should only be one
   1201             assertFalse(c.moveToNext());
   1202         } finally {
   1203             c.close();
   1204         }
   1205 
   1206         // delete the second message
   1207         cr.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, message2Id), null, null);
   1208 
   1209         // hey, presto! the change should be gone
   1210         numMessages = EmailContent.count(mMockContext, Message.UPDATED_CONTENT_URI, selection,
   1211                 selArgs);
   1212         assertEquals(0, numMessages);
   1213 
   1214         // and there should now be a deleted record
   1215         numMessages = EmailContent.count(mMockContext, Message.DELETED_CONTENT_URI, selection,
   1216                 selArgs);
   1217         assertEquals(1, numMessages);
   1218     }
   1219 
   1220     /**
   1221      * TODO: cascaded delete account
   1222      * TODO: hostauth
   1223      * TODO: body
   1224      * TODO: attachments
   1225      * TODO: create other account, mailbox & messages and confirm the right objects were deleted
   1226      */
   1227     public void testCascadeDeleteAccount() {
   1228         Account account1 = ProviderTestUtils.setupAccount("account-delete-cascade", true,
   1229                 mMockContext);
   1230         long account1Id = account1.mId;
   1231         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
   1232         long box1Id = box1.mId;
   1233         /* Message message1 = */ ProviderTestUtils.setupMessage("message1", account1Id, box1Id,
   1234                 false, true, mMockContext);
   1235         /* Message message2 = */ ProviderTestUtils.setupMessage("message2", account1Id, box1Id,
   1236                 false, true, mMockContext);
   1237 
   1238         // make sure there is one account, one mailbox, and two messages
   1239         int numAccounts = EmailContent.count(mMockContext, Account.CONTENT_URI, null, null);
   1240         assertEquals(1, numAccounts);
   1241         int numBoxes = EmailContent.count(mMockContext, Mailbox.CONTENT_URI, null, null);
   1242         assertEquals(1, numBoxes);
   1243         int numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, null, null);
   1244         assertEquals(2, numMessages);
   1245 
   1246         // delete the account
   1247         Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, account1Id);
   1248         mMockContext.getContentResolver().delete(uri, null, null);
   1249 
   1250         // make sure there are no accounts, mailboxes, or messages
   1251         numAccounts = EmailContent.count(mMockContext, Account.CONTENT_URI, null, null);
   1252         assertEquals(0, numAccounts);
   1253         numBoxes = EmailContent.count(mMockContext, Mailbox.CONTENT_URI, null, null);
   1254         assertEquals(0, numBoxes);
   1255         numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, null, null);
   1256         assertEquals(0, numMessages);
   1257     }
   1258 
   1259     /**
   1260      * Test cascaded delete mailbox
   1261      * TODO: body
   1262      * TODO: attachments
   1263      * TODO: create other mailbox & messages and confirm the right objects were deleted
   1264      */
   1265     public void testCascadeDeleteMailbox() {
   1266         Account account1 = ProviderTestUtils.setupAccount("mailbox-delete-cascade", true,
   1267                 mMockContext);
   1268         long account1Id = account1.mId;
   1269         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
   1270         long box1Id = box1.mId;
   1271         Message message1 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id,
   1272                 false, true, mMockContext);
   1273         Message message2 = ProviderTestUtils.setupMessage("message2", account1Id, box1Id,
   1274                 false, true, mMockContext);
   1275         Message message3 = ProviderTestUtils.setupMessage("message3", account1Id, box1Id,
   1276                 false, true, mMockContext);
   1277         Message message4 = ProviderTestUtils.setupMessage("message4", account1Id, box1Id,
   1278                 false, true, mMockContext);
   1279         ProviderTestUtils.setupMessage("message5", account1Id, box1Id, false, true, mMockContext);
   1280         ProviderTestUtils.setupMessage("message6", account1Id, box1Id, false, true, mMockContext);
   1281 
   1282         String selection = EmailContent.MessageColumns.ACCOUNT_KEY + "=? AND " +
   1283                 EmailContent.MessageColumns.MAILBOX_KEY + "=?";
   1284         String[] selArgs = new String[] { String.valueOf(account1Id), String.valueOf(box1Id) };
   1285 
   1286         // make sure there are six messages
   1287         int numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, selection, selArgs);
   1288         assertEquals(6, numMessages);
   1289 
   1290         ContentValues cv = new ContentValues();
   1291         cv.put(Message.SERVER_ID, "SERVER_ID");
   1292         ContentResolver resolver = mMockContext.getContentResolver();
   1293 
   1294         // Update two messages
   1295         resolver.update(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, message1.mId),
   1296                 cv, null, null);
   1297         resolver.update(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, message2.mId),
   1298                 cv, null, null);
   1299         // Delete two messages
   1300         resolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, message3.mId),
   1301                 null, null);
   1302         resolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, message4.mId),
   1303                 null, null);
   1304 
   1305         // There should now be two messages in updated/deleted, and 4 in messages
   1306         numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, selection, selArgs);
   1307         assertEquals(4, numMessages);
   1308         numMessages = EmailContent.count(mMockContext, Message.DELETED_CONTENT_URI, selection,
   1309                 selArgs);
   1310         assertEquals(2, numMessages);
   1311         numMessages = EmailContent.count(mMockContext, Message.UPDATED_CONTENT_URI, selection,
   1312                 selArgs);
   1313         assertEquals(2, numMessages);
   1314 
   1315         // now delete the mailbox
   1316         Uri uri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, box1Id);
   1317         resolver.delete(uri, null, null);
   1318 
   1319         // there should now be zero messages in all three tables
   1320         numMessages = EmailContent.count(mMockContext, Message.CONTENT_URI, selection, selArgs);
   1321         assertEquals(0, numMessages);
   1322         numMessages = EmailContent.count(mMockContext, Message.DELETED_CONTENT_URI, selection,
   1323                 selArgs);
   1324         assertEquals(0, numMessages);
   1325         numMessages = EmailContent.count(mMockContext, Message.UPDATED_CONTENT_URI, selection,
   1326                 selArgs);
   1327         assertEquals(0, numMessages);
   1328     }
   1329 
   1330     /**
   1331      * Test cascaded delete message
   1332      * Confirms that deleting a message will also delete its body & attachments
   1333      */
   1334     public void testCascadeMessageDelete() {
   1335         Account account1 = ProviderTestUtils.setupAccount("message-cascade", true, mMockContext);
   1336         long account1Id = account1.mId;
   1337         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", account1Id, true, mMockContext);
   1338         long box1Id = box1.mId;
   1339 
   1340         // Each message has a body, and also give each 2 attachments
   1341         Message message1 = ProviderTestUtils.setupMessage("message1", account1Id, box1Id, true,
   1342                 false, mMockContext);
   1343         ArrayList<Attachment> atts = new ArrayList<Attachment>();
   1344         for (int i = 0; i < 2; i++) {
   1345             atts.add(ProviderTestUtils.setupAttachment(
   1346                     -1, expectedAttachmentNames[i], expectedAttachmentSizes[i],
   1347                     false, mMockContext));
   1348         }
   1349         message1.mAttachments = atts;
   1350         message1.save(mMockContext);
   1351         long message1Id = message1.mId;
   1352 
   1353         Message message2 = ProviderTestUtils.setupMessage("message2", account1Id, box1Id, true,
   1354                 false, mMockContext);
   1355         atts = new ArrayList<Attachment>();
   1356         for (int i = 0; i < 2; i++) {
   1357             atts.add(ProviderTestUtils.setupAttachment(
   1358                     -1, expectedAttachmentNames[i], expectedAttachmentSizes[i],
   1359                     false, mMockContext));
   1360         }
   1361         message2.mAttachments = atts;
   1362         message2.save(mMockContext);
   1363         long message2Id = message2.mId;
   1364 
   1365         // Set up to test total counts of bodies & attachments for our test messages
   1366         String bodySelection = BodyColumns.MESSAGE_KEY + " IN (?,?)";
   1367         String attachmentSelection = AttachmentColumns.MESSAGE_KEY + " IN (?,?)";
   1368         String[] selArgs = new String[] { String.valueOf(message1Id), String.valueOf(message2Id) };
   1369 
   1370         // make sure there are two bodies
   1371         int numBodies = EmailContent.count(mMockContext, Body.CONTENT_URI, bodySelection, selArgs);
   1372         assertEquals(2, numBodies);
   1373 
   1374         // make sure there are four attachments
   1375         int numAttachments = EmailContent.count(mMockContext, Attachment.CONTENT_URI,
   1376                 attachmentSelection, selArgs);
   1377         assertEquals(4, numAttachments);
   1378 
   1379         // now delete one of the messages
   1380         Uri uri = ContentUris.withAppendedId(Message.CONTENT_URI, message1Id);
   1381         mMockContext.getContentResolver().delete(uri, null, null);
   1382 
   1383         // there should be one body and two attachments
   1384         numBodies = EmailContent.count(mMockContext, Body.CONTENT_URI, bodySelection, selArgs);
   1385         assertEquals(1, numBodies);
   1386 
   1387         numAttachments = EmailContent.count(mMockContext, Attachment.CONTENT_URI,
   1388                 attachmentSelection, selArgs);
   1389         assertEquals(2, numAttachments);
   1390 
   1391         // now delete the other message
   1392         uri = ContentUris.withAppendedId(Message.CONTENT_URI, message2Id);
   1393         mMockContext.getContentResolver().delete(uri, null, null);
   1394 
   1395         // make sure there are no bodies or attachments
   1396         numBodies = EmailContent.count(mMockContext, Body.CONTENT_URI, bodySelection, selArgs);
   1397         assertEquals(0, numBodies);
   1398 
   1399         numAttachments = EmailContent.count(mMockContext, Attachment.CONTENT_URI,
   1400                 attachmentSelection, selArgs);
   1401         assertEquals(0, numAttachments);
   1402     }
   1403 
   1404     /**
   1405      * Test that our unique file name algorithm works as expected.  Since this test requires an
   1406      * SD card, we check the environment first, and return immediately if none is mounted.
   1407      * @throws IOException
   1408      */
   1409     public void testCreateUniqueFile() throws IOException {
   1410         // Delete existing files, if they exist
   1411         if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
   1412             return;
   1413         }
   1414         try {
   1415             String fileName = "A11achm3n1.doc";
   1416             File uniqueFile = Attachment.createUniqueFile(fileName);
   1417             assertEquals(fileName, uniqueFile.getName());
   1418             if (uniqueFile.createNewFile()) {
   1419                 uniqueFile = Attachment.createUniqueFile(fileName);
   1420                 assertEquals("A11achm3n1-2.doc", uniqueFile.getName());
   1421                 if (uniqueFile.createNewFile()) {
   1422                     uniqueFile = Attachment.createUniqueFile(fileName);
   1423                     assertEquals("A11achm3n1-3.doc", uniqueFile.getName());
   1424                 }
   1425            }
   1426             fileName = "A11achm3n1";
   1427             uniqueFile = Attachment.createUniqueFile(fileName);
   1428             assertEquals(fileName, uniqueFile.getName());
   1429             if (uniqueFile.createNewFile()) {
   1430                 uniqueFile = Attachment.createUniqueFile(fileName);
   1431                 assertEquals("A11achm3n1-2", uniqueFile.getName());
   1432             }
   1433         } finally {
   1434             File directory = Environment.getExternalStorageDirectory();
   1435             // These are the files that should be created earlier in the test.  Make sure
   1436             // they are deleted for the next go-around
   1437             String[] fileNames = new String[] {"A11achm3n1.doc", "A11achm3n1-2.doc", "A11achm3n1"};
   1438             int length = fileNames.length;
   1439             for (int i = 0; i < length; i++) {
   1440                 File file = new File(directory, fileNames[i]);
   1441                 if (file.exists()) {
   1442                     file.delete();
   1443                 }
   1444             }
   1445         }
   1446     }
   1447 
   1448     /**
   1449      * Test retrieving attachments by message ID (using EmailContent.Attachment.MESSAGE_ID_URI)
   1450      */
   1451     public void testGetAttachmentByMessageIdUri() {
   1452 
   1453         // Note, we don't strictly need accounts, mailboxes or messages to run this test.
   1454         Attachment a1 = ProviderTestUtils.setupAttachment(1, "a1", 100, true, mMockContext);
   1455         Attachment a2 = ProviderTestUtils.setupAttachment(1, "a2", 200, true, mMockContext);
   1456         ProviderTestUtils.setupAttachment(2, "a3", 300, true, mMockContext);
   1457         ProviderTestUtils.setupAttachment(2, "a4", 400, true, mMockContext);
   1458 
   1459         // Now ask for the attachments of message id=1
   1460         // Note: Using the "sort by size" trick to bring them back in expected order
   1461         Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, 1);
   1462         Cursor c = mMockContext.getContentResolver().query(uri, Attachment.CONTENT_PROJECTION,
   1463                 null, null, Attachment.SIZE);
   1464         assertEquals(2, c.getCount());
   1465 
   1466         try {
   1467             c.moveToFirst();
   1468             Attachment a1Get = EmailContent.getContent(c, Attachment.class);
   1469             ProviderTestUtils.assertAttachmentEqual("getAttachByUri-1", a1, a1Get);
   1470             c.moveToNext();
   1471             Attachment a2Get = EmailContent.getContent(c, Attachment.class);
   1472             ProviderTestUtils.assertAttachmentEqual("getAttachByUri-2", a2, a2Get);
   1473         } finally {
   1474             c.close();
   1475         }
   1476     }
   1477 
   1478     /**
   1479      * Test deleting attachments by message ID (using EmailContent.Attachment.MESSAGE_ID_URI)
   1480      */
   1481     public void testDeleteAttachmentByMessageIdUri() {
   1482         ContentResolver mockResolver = mMockContext.getContentResolver();
   1483 
   1484         // Note, we don't strictly need accounts, mailboxes or messages to run this test.
   1485         ProviderTestUtils.setupAttachment(1, "a1", 100, true, mMockContext);
   1486         ProviderTestUtils.setupAttachment(1, "a2", 200, true, mMockContext);
   1487         Attachment a3 = ProviderTestUtils.setupAttachment(2, "a3", 300, true, mMockContext);
   1488         Attachment a4 = ProviderTestUtils.setupAttachment(2, "a4", 400, true, mMockContext);
   1489 
   1490         // Delete all attachments for message id=1
   1491         Uri uri = ContentUris.withAppendedId(Attachment.MESSAGE_ID_URI, 1);
   1492         mockResolver.delete(uri, null, null);
   1493 
   1494         // Read back all attachments and confirm that we have the expected remaining attachments
   1495         // (the attachments that are set for message id=2).  Note order-by size to simplify test.
   1496         Cursor c = mockResolver.query(Attachment.CONTENT_URI, Attachment.CONTENT_PROJECTION,
   1497                 null, null, Attachment.SIZE);
   1498         assertEquals(2, c.getCount());
   1499 
   1500         try {
   1501             c.moveToFirst();
   1502             Attachment a3Get = EmailContent.getContent(c, Attachment.class);
   1503             ProviderTestUtils.assertAttachmentEqual("getAttachByUri-3", a3, a3Get);
   1504             c.moveToNext();
   1505             Attachment a4Get = EmailContent.getContent(c, Attachment.class);
   1506             ProviderTestUtils.assertAttachmentEqual("getAttachByUri-4", a4, a4Get);
   1507         } finally {
   1508             c.close();
   1509         }
   1510     }
   1511 
   1512     @SmallTest
   1513     public void testGetDefaultAccountNoneExplicitlySet() {
   1514         Account account1 = ProviderTestUtils.setupAccount("account-default-1", false, mMockContext);
   1515         account1.mIsDefault = false;
   1516         account1.save(mMockContext);
   1517 
   1518         // We should find account1 as default
   1519         long defaultAccountId = Account.getDefaultAccountId(mMockContext);
   1520         assertEquals(defaultAccountId, account1.mId);
   1521 
   1522         Account account2 = ProviderTestUtils.setupAccount("account-default-2", false, mMockContext);
   1523         account2.mIsDefault = false;
   1524         account2.save(mMockContext);
   1525 
   1526         Account account3 = ProviderTestUtils.setupAccount("account-default-3", false, mMockContext);
   1527         account3.mIsDefault = false;
   1528         account3.save(mMockContext);
   1529 
   1530         // We should find the earliest one as the default, so that it can be consistent on
   1531         // repeated calls.
   1532         defaultAccountId = Account.getDefaultAccountId(mMockContext);
   1533         assertTrue(defaultAccountId == account1.mId);
   1534     }
   1535 
   1536     /**
   1537      * Tests of default account behavior
   1538      *
   1539      * 1.  Simple set/get
   1540      * 2.  Moving default between 3 accounts
   1541      * 3.  Delete default, make sure another becomes default
   1542      */
   1543     public void testSetGetDefaultAccount() {
   1544         // There should be no default account if there are no accounts
   1545         long defaultAccountId = Account.getDefaultAccountId(mMockContext);
   1546         assertEquals(Account.NO_ACCOUNT, defaultAccountId);
   1547 
   1548         Account account1 = ProviderTestUtils.setupAccount("account-default-1", false, mMockContext);
   1549         account1.mIsDefault = false;
   1550         account1.save(mMockContext);
   1551         long account1Id = account1.mId;
   1552         Account account2 = ProviderTestUtils.setupAccount("account-default-2", false, mMockContext);
   1553         account2.mIsDefault = false;
   1554         account2.save(mMockContext);
   1555         long account2Id = account2.mId;
   1556         Account account3 = ProviderTestUtils.setupAccount("account-default-3", false, mMockContext);
   1557         account3.mIsDefault = false;
   1558         account3.save(mMockContext);
   1559         long account3Id = account3.mId;
   1560 
   1561         // With three accounts, but none marked default, confirm that the first one is the default.
   1562         defaultAccountId = Account.getDefaultAccountId(mMockContext);
   1563         assertTrue(defaultAccountId == account1Id);
   1564 
   1565         updateIsDefault(account1, true);
   1566         defaultAccountId = Account.getDefaultAccountId(mMockContext);
   1567         assertEquals(account1Id, defaultAccountId);
   1568 
   1569         updateIsDefault(account2, true);
   1570         defaultAccountId = Account.getDefaultAccountId(mMockContext);
   1571         assertEquals(account2Id, defaultAccountId);
   1572 
   1573         updateIsDefault(account3, true);
   1574         defaultAccountId = Account.getDefaultAccountId(mMockContext);
   1575         assertEquals(account3Id, defaultAccountId);
   1576 
   1577         // Now delete a non-default account and confirm no change
   1578         Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, account1Id);
   1579         mMockContext.getContentResolver().delete(uri, null, null);
   1580 
   1581         defaultAccountId = Account.getDefaultAccountId(mMockContext);
   1582         assertEquals(account3Id, defaultAccountId);
   1583 
   1584         // Now confirm deleting the default account and it switches to another one
   1585         uri = ContentUris.withAppendedId(Account.CONTENT_URI, account3Id);
   1586         mMockContext.getContentResolver().delete(uri, null, null);
   1587 
   1588         defaultAccountId = Account.getDefaultAccountId(mMockContext);
   1589         assertEquals(account2Id, defaultAccountId);
   1590 
   1591         // Now delete the final account and confirm there are no default accounts again
   1592         uri = ContentUris.withAppendedId(Account.CONTENT_URI, account2Id);
   1593         mMockContext.getContentResolver().delete(uri, null, null);
   1594 
   1595         defaultAccountId = Account.getDefaultAccountId(mMockContext);
   1596         assertEquals(-1, defaultAccountId);
   1597     }
   1598 
   1599     private void updateIsDefault(Account account, boolean newState) {
   1600         account.setDefaultAccount(newState);
   1601         ContentValues cv = new ContentValues();
   1602         cv.put(AccountColumns.IS_DEFAULT, account.mIsDefault);
   1603         account.update(mMockContext, cv);
   1604     }
   1605 
   1606     public static Message setupUnreadMessage(String name, long accountId, long mailboxId,
   1607             boolean addBody, boolean saveIt, Context context) {
   1608         Message msg =
   1609             ProviderTestUtils.setupMessage(name, accountId, mailboxId, addBody, false, context);
   1610         msg.mFlagRead = false;
   1611         if (saveIt) {
   1612             msg.save(context);
   1613         }
   1614         return msg;
   1615     }
   1616 
   1617     public void testUnreadCountTriggers() {
   1618         // Start with one account and three mailboxes
   1619         Account account = ProviderTestUtils.setupAccount("triggers", true, mMockContext);
   1620         Mailbox boxA = ProviderTestUtils.setupMailbox("boxA", account.mId, true, mMockContext);
   1621         Mailbox boxB = ProviderTestUtils.setupMailbox("boxB", account.mId, true, mMockContext);
   1622         Mailbox boxC = ProviderTestUtils.setupMailbox("boxC", account.mId, true, mMockContext);
   1623 
   1624         // Make sure there are no unreads
   1625         assertEquals(0, getUnreadCount(boxA.mId));
   1626         assertEquals(0, getUnreadCount(boxB.mId));
   1627         assertEquals(0, getUnreadCount(boxC.mId));
   1628 
   1629         // Create 4 unread messages (only 3 named) in boxA
   1630         Message message1 = setupUnreadMessage("message1", account.mId, boxA.mId,
   1631                 false, true, mMockContext);
   1632         Message message2= setupUnreadMessage("message2", account.mId, boxA.mId,
   1633                 false, true, mMockContext);
   1634         Message message3 =  setupUnreadMessage("message3", account.mId, boxA.mId,
   1635                 false, true, mMockContext);
   1636         setupUnreadMessage("message4", account.mId, boxC.mId, false, true, mMockContext);
   1637 
   1638         // Make sure the unreads are where we expect them
   1639         assertEquals(3, getUnreadCount(boxA.mId));
   1640         assertEquals(0, getUnreadCount(boxB.mId));
   1641         assertEquals(1, getUnreadCount(boxC.mId));
   1642 
   1643         // After deleting message 1, the count in box A should be decremented (to 2)
   1644         ContentResolver cr = mMockContext.getContentResolver();
   1645         Uri uri = ContentUris.withAppendedId(Message.CONTENT_URI, message1.mId);
   1646         cr.delete(uri, null, null);
   1647         assertEquals(2, getUnreadCount(boxA.mId));
   1648         assertEquals(0, getUnreadCount(boxB.mId));
   1649         assertEquals(1, getUnreadCount(boxC.mId));
   1650 
   1651         // Move message 2 to box B, leaving 1 in box A and 1 in box B
   1652         message2.mMailboxKey = boxB.mId;
   1653         ContentValues cv = new ContentValues();
   1654         cv.put(MessageColumns.MAILBOX_KEY, boxB.mId);
   1655         cr.update(ContentUris.withAppendedId(Message.CONTENT_URI, message2.mId), cv, null, null);
   1656         assertEquals(1, getUnreadCount(boxA.mId));
   1657         assertEquals(1, getUnreadCount(boxB.mId));
   1658         assertEquals(1, getUnreadCount(boxC.mId));
   1659 
   1660         // Mark message 3 (from box A) read, leaving 0 in box A
   1661         cv.clear();
   1662         cv.put(MessageColumns.FLAG_READ, 1);
   1663         cr.update(ContentUris.withAppendedId(Message.CONTENT_URI, message3.mId), cv, null, null);
   1664         assertEquals(0, getUnreadCount(boxA.mId));
   1665         assertEquals(1, getUnreadCount(boxB.mId));
   1666         assertEquals(1, getUnreadCount(boxC.mId));
   1667 
   1668         // Move message 3 to box C; should be no change (it's read)
   1669         message3.mMailboxKey = boxC.mId;
   1670         cv.clear();
   1671         cv.put(MessageColumns.MAILBOX_KEY, boxC.mId);
   1672         cr.update(ContentUris.withAppendedId(Message.CONTENT_URI, message3.mId), cv, null, null);
   1673         assertEquals(0, getUnreadCount(boxA.mId));
   1674         assertEquals(1, getUnreadCount(boxB.mId));
   1675         assertEquals(1, getUnreadCount(boxC.mId));
   1676 
   1677         // Mark message 3 unread; it's now in box C, so that box's count should go up to 3
   1678         cv.clear();
   1679         cv.put(MessageColumns.FLAG_READ, 0);
   1680         cr.update(ContentUris.withAppendedId(Message.CONTENT_URI, message3.mId), cv, null, null);
   1681         assertEquals(0, getUnreadCount(boxA.mId));
   1682         assertEquals(1, getUnreadCount(boxB.mId));
   1683         assertEquals(2, getUnreadCount(boxC.mId));
   1684     }
   1685 
   1686     /**
   1687      * Test for EmailProvider.createIndex().
   1688      * Check that it returns exacly the same string as the one used previously for index creation.
   1689      */
   1690     public void testCreateIndex() {
   1691         String oldStr = "create index message_" + MessageColumns.TIMESTAMP
   1692             + " on " + Message.TABLE_NAME + " (" + MessageColumns.TIMESTAMP + ");";
   1693         String newStr = EmailProvider.createIndex(Message.TABLE_NAME, MessageColumns.TIMESTAMP);
   1694         assertEquals(newStr, oldStr);
   1695     }
   1696 
   1697     public void testDatabaseCorruptionRecovery() {
   1698         final ContentResolver resolver = mMockContext.getContentResolver();
   1699         final Context context = mMockContext;
   1700 
   1701         // Create account and two mailboxes
   1702         Account acct = ProviderTestUtils.setupAccount("acct1", true, context);
   1703         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", acct.mId, true, context);
   1704 
   1705         // Create 4 messages in box1 with bodies
   1706         ProviderTestUtils.setupMessage("message1", acct.mId, box1.mId, true, true, context);
   1707         ProviderTestUtils.setupMessage("message2", acct.mId, box1.mId, true, true, context);
   1708         ProviderTestUtils.setupMessage("message3", acct.mId, box1.mId, true, true, context);
   1709         ProviderTestUtils.setupMessage("message4", acct.mId, box1.mId, true, true, context);
   1710 
   1711         // Confirm there are four messages
   1712         int count = EmailContent.count(mMockContext, Message.CONTENT_URI, null, null);
   1713         assertEquals(4, count);
   1714         // Confirm there are four bodies
   1715         count = EmailContent.count(mMockContext, Body.CONTENT_URI, null, null);
   1716         assertEquals(4, count);
   1717 
   1718         // Find the EmailProvider.db file
   1719         File dbFile = mMockContext.getDatabasePath(EmailProvider.DATABASE_NAME);
   1720         // The EmailProvider.db database should exist (the provider creates it automatically)
   1721         assertTrue(dbFile != null);
   1722         assertTrue(dbFile.exists());
   1723         // Delete it, and confirm it is gone
   1724         assertTrue(dbFile.delete());
   1725         assertFalse(dbFile.exists());
   1726 
   1727         // Find the EmailProviderBody.db file
   1728         dbFile = mMockContext.getDatabasePath(EmailProvider.BODY_DATABASE_NAME);
   1729         // The EmailProviderBody.db database should still exist
   1730         assertTrue(dbFile != null);
   1731         assertTrue(dbFile.exists());
   1732 
   1733         // URI to uncache the databases
   1734         // This simulates the Provider starting up again (otherwise, it will still be pointing to
   1735         // the already opened files)
   1736         // Note that we only have access to the EmailProvider via the ContentResolver; therefore,
   1737         // we cannot directly call into the provider and use a URI for this
   1738         resolver.update(EmailProvider.INTEGRITY_CHECK_URI, null, null, null);
   1739 
   1740         // TODO We should check for the deletion of attachment files once this is implemented in
   1741         // the provider
   1742 
   1743         // Explanation for what happens below...
   1744         // The next time the database is created by the provider, it will notice that there's
   1745         // already a EmailProviderBody.db file.  In this case, it will delete that database to
   1746         // ensure that both are in sync (and empty)
   1747 
   1748         // Confirm there are no bodies
   1749         count = EmailContent.count(mMockContext, Body.CONTENT_URI, null, null);
   1750         assertEquals(0, count);
   1751 
   1752         // Confirm there are no messages
   1753         count = EmailContent.count(mMockContext, Message.CONTENT_URI, null, null);
   1754         assertEquals(0, count);
   1755     }
   1756 
   1757     public void testBodyDatabaseCorruptionRecovery() {
   1758         final ContentResolver resolver = mMockContext.getContentResolver();
   1759         final Context context = mMockContext;
   1760 
   1761         // Create account and two mailboxes
   1762         Account acct = ProviderTestUtils.setupAccount("acct1", true, context);
   1763         Mailbox box1 = ProviderTestUtils.setupMailbox("box1", acct.mId, true, context);
   1764 
   1765         // Create 4 messages in box1 with bodies
   1766         ProviderTestUtils.setupMessage("message1", acct.mId, box1.mId, true, true, context);
   1767         ProviderTestUtils.setupMessage("message2", acct.mId, box1.mId, true, true, context);
   1768         ProviderTestUtils.setupMessage("message3", acct.mId, box1.mId, true, true, context);
   1769         ProviderTestUtils.setupMessage("message4", acct.mId, box1.mId, true, true, context);
   1770 
   1771         // Confirm there are four messages
   1772         int count = EmailContent.count(mMockContext, Message.CONTENT_URI, null, null);
   1773         assertEquals(4, count);
   1774         // Confirm there are four bodies
   1775         count = EmailContent.count(mMockContext, Body.CONTENT_URI, null, null);
   1776         assertEquals(4, count);
   1777 
   1778         // Find the EmailProviderBody.db file
   1779         File dbFile = mMockContext.getDatabasePath(EmailProvider.BODY_DATABASE_NAME);
   1780         // The EmailProviderBody.db database should exist (the provider creates it automatically)
   1781         assertTrue(dbFile != null);
   1782         assertTrue(dbFile.exists());
   1783         // Delete it, and confirm it is gone
   1784         assertTrue(dbFile.delete());
   1785         assertFalse(dbFile.exists());
   1786 
   1787         // Find the EmailProvider.db file
   1788         dbFile = mMockContext.getDatabasePath(EmailProvider.DATABASE_NAME);
   1789         // The EmailProviderBody.db database should still exist
   1790         assertTrue(dbFile != null);
   1791         assertTrue(dbFile.exists());
   1792 
   1793         // URI to uncache the databases
   1794         // This simulates the Provider starting up again (otherwise, it will still be pointing to
   1795         // the already opened files)
   1796         // Note that we only have access to the EmailProvider via the ContentResolver; therefore,
   1797         // we cannot directly call into the provider and use a URI for this
   1798         resolver.update(EmailProvider.INTEGRITY_CHECK_URI, null, null, null);
   1799 
   1800         // TODO We should check for the deletion of attachment files once this is implemented in
   1801         // the provider
   1802 
   1803         // Explanation for what happens below...
   1804         // The next time the body database is created by the provider, it will notice that there's
   1805         // already a populated EmailProvider.db file.  In this case, it will delete that database to
   1806         // ensure that both are in sync (and empty)
   1807 
   1808         // Confirm there are no messages
   1809         count = EmailContent.count(mMockContext, Message.CONTENT_URI, null, null);
   1810         assertEquals(0, count);
   1811 
   1812         // Confirm there are no bodies
   1813         count = EmailContent.count(mMockContext, Body.CONTENT_URI, null, null);
   1814         assertEquals(0, count);
   1815     }
   1816 
   1817     public void testAccountIsSecurityHold() {
   1818         final Context context = mMockContext;
   1819         Account acct1 = ProviderTestUtils.setupAccount("acct1", true, context);
   1820 
   1821         Account acct2 = ProviderTestUtils.setupAccount("acct2", false, context);
   1822         acct2.mFlags |= Account.FLAGS_SECURITY_HOLD;
   1823         acct2.save(context);
   1824 
   1825         assertFalse(Account.isSecurityHold(context, acct1.mId));
   1826         assertTrue(Account.isSecurityHold(context, acct2.mId));
   1827         assertFalse(Account.isSecurityHold(context, 9999999)); // No such account
   1828    }
   1829 
   1830     public void testClearAccountHoldFlags() {
   1831         Account a1 = ProviderTestUtils.setupAccount("holdflag-1", false, mMockContext);
   1832         a1.mFlags = Account.FLAGS_NOTIFY_NEW_MAIL;
   1833         a1.mPolicy = new Policy();
   1834         a1.save(mMockContext);
   1835         Account a2 = ProviderTestUtils.setupAccount("holdflag-2", false, mMockContext);
   1836         a2.mFlags = Account.FLAGS_VIBRATE_ALWAYS | Account.FLAGS_SECURITY_HOLD;
   1837         a2.mPolicy = new Policy();
   1838         a2.save(mMockContext);
   1839 
   1840         // bulk clear
   1841         Account.clearSecurityHoldOnAllAccounts(mMockContext);
   1842 
   1843         // confirm new values as expected - no hold flags; other flags unmolested
   1844         Account a1a = Account.restoreAccountWithId(mMockContext, a1.mId);
   1845         assertEquals(Account.FLAGS_NOTIFY_NEW_MAIL, a1a.mFlags);
   1846         Account a2a = Account.restoreAccountWithId(mMockContext, a2.mId);
   1847         assertEquals(Account.FLAGS_VIBRATE_ALWAYS, a2a.mFlags);
   1848     }
   1849 
   1850     private static Message createMessage(Context c, Mailbox b, boolean starred, boolean read) {
   1851         return ProviderTestUtils.setupMessage(
   1852                 "1", b.mAccountKey, b.mId, true, true, c, starred, read);
   1853     }
   1854 
   1855     public void testAccountIsEasAccount() {
   1856         Account account = new Account();
   1857         // No hostauth
   1858         assertFalse(account.isEasAccount(mMockContext));
   1859 
   1860         checkAccountIsEasAccount(null, false);
   1861         checkAccountIsEasAccount("", false);
   1862         checkAccountIsEasAccount("x", false);
   1863         checkAccountIsEasAccount("eas", true);
   1864     }
   1865 
   1866     private void checkAccountIsEasAccount(String protocol, boolean expected) {
   1867         Account account = ProviderTestUtils.setupAccount("account", false, mMockContext);
   1868         account.mHostAuthRecv = ProviderTestUtils.setupHostAuth(protocol, "account-hostauth-recv",
   1869                 false, mMockContext);
   1870         account.save(mMockContext);
   1871         assertEquals(expected, account.isEasAccount(mMockContext));
   1872     }
   1873 
   1874     public void testGetKeyColumnLong() {
   1875         final Context c = mMockContext;
   1876         Account a = ProviderTestUtils.setupAccount("acct", true, c);
   1877         Mailbox b1 = ProviderTestUtils.setupMailbox("box1", a.mId, true, c, Mailbox.TYPE_MAIL);
   1878         Mailbox b2 = ProviderTestUtils.setupMailbox("box2", a.mId, true, c, Mailbox.TYPE_MAIL);
   1879         Message m1 = createMessage(c, b1, false, false);
   1880         Message m2 = createMessage(c, b2, false, false);
   1881         assertEquals(a.mId, Message.getKeyColumnLong(c, m1.mId, MessageColumns.ACCOUNT_KEY));
   1882         assertEquals(a.mId, Message.getKeyColumnLong(c, m2.mId, MessageColumns.ACCOUNT_KEY));
   1883         assertEquals(b1.mId, Message.getKeyColumnLong(c, m1.mId, MessageColumns.MAILBOX_KEY));
   1884         assertEquals(b2.mId, Message.getKeyColumnLong(c, m2.mId, MessageColumns.MAILBOX_KEY));
   1885     }
   1886 
   1887     public void testGetAccountIdForMessageId() {
   1888         final Context c = mMockContext;
   1889         Account a1 = ProviderTestUtils.setupAccount("acct1", true, c);
   1890         Account a2 = ProviderTestUtils.setupAccount("acct2", true, c);
   1891         Mailbox b1 = ProviderTestUtils.setupMailbox("box1", a1.mId, true, c, Mailbox.TYPE_MAIL);
   1892         Mailbox b2 = ProviderTestUtils.setupMailbox("box2", a2.mId, true, c, Mailbox.TYPE_MAIL);
   1893         Message m1 = createMessage(c, b1, false, false);
   1894         Message m2 = createMessage(c, b2, false, false);
   1895 
   1896         assertEquals(a1.mId, Account.getAccountIdForMessageId(c, m1.mId));
   1897         assertEquals(a2.mId, Account.getAccountIdForMessageId(c, m2.mId));
   1898 
   1899         // message desn't exist
   1900         assertEquals(-1, Account.getAccountIdForMessageId(c, 12345));
   1901     }
   1902 
   1903     public void testGetAccountForMessageId() {
   1904         final Context c = mMockContext;
   1905         Account a = ProviderTestUtils.setupAccount("acct", true, c);
   1906         Message m1 = ProviderTestUtils.setupMessage("1", a.mId, 1, true, true, c, false, false);
   1907         Message m2 = ProviderTestUtils.setupMessage("1", a.mId, 2, true, true, c, false, false);
   1908         ProviderTestUtils.assertAccountEqual("x", a, Account.getAccountForMessageId(c, m1.mId));
   1909         ProviderTestUtils.assertAccountEqual("x", a, Account.getAccountForMessageId(c, m2.mId));
   1910     }
   1911 
   1912     public void testGetAccountGetInboxIdTest() {
   1913         final Context c = mMockContext;
   1914 
   1915         // Prepare some data with red-herrings.
   1916         Account a1 = ProviderTestUtils.setupAccount("acct1", true, c);
   1917         Account a2 = ProviderTestUtils.setupAccount("acct2", true, c);
   1918         Mailbox b1i = ProviderTestUtils.setupMailbox("b1i", a1.mId, true, c, Mailbox.TYPE_INBOX);
   1919         Mailbox b2a = ProviderTestUtils.setupMailbox("b2a", a2.mId, true, c, Mailbox.TYPE_MAIL);
   1920         Mailbox b2i = ProviderTestUtils.setupMailbox("b2b", a2.mId, true, c, Mailbox.TYPE_INBOX);
   1921 
   1922         assertEquals(b2i.mId, Account.getInboxId(c, a2.mId));
   1923 
   1924         // No account found.
   1925         assertEquals(-1, Account.getInboxId(c, 999999));
   1926     }
   1927 
   1928     /**
   1929      * Check if update to {@link Account#RESET_NEW_MESSAGE_COUNT_URI} resets the new message count.
   1930      */
   1931     public void testResetNewMessageCount() {
   1932         final Context c = mMockContext;
   1933         final ContentResolver cr = c.getContentResolver();
   1934 
   1935         // Prepare test data
   1936         Account a1 = ProviderTestUtils.setupAccount("acct1", false, c);
   1937         a1.mNewMessageCount = 1;
   1938         a1.save(c);
   1939         Account a2 = ProviderTestUtils.setupAccount("acct2", false, c);
   1940         a2.mNewMessageCount = 2;
   1941         a2.save(c);
   1942         Account a3 = ProviderTestUtils.setupAccount("acct3", false, c);
   1943         a3.mNewMessageCount = 3;
   1944         a3.save(c);
   1945         Account a4 = ProviderTestUtils.setupAccount("acct4", false, c);
   1946         a4.mNewMessageCount = 4;
   1947         a4.save(c);
   1948         Account a5 = ProviderTestUtils.setupAccount("acct5", false, c);
   1949         a5.mNewMessageCount = 5;
   1950         a5.save(c);
   1951 
   1952         // With ID in URI, no selection
   1953         cr.update(ContentUris.withAppendedId(Account.RESET_NEW_MESSAGE_COUNT_URI, a1.mId),
   1954                 null, null, null);
   1955         assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount);
   1956         assertEquals(2, Account.restoreAccountWithId(c, a2.mId).mNewMessageCount);
   1957         assertEquals(3, Account.restoreAccountWithId(c, a3.mId).mNewMessageCount);
   1958         assertEquals(4, Account.restoreAccountWithId(c, a4.mId).mNewMessageCount);
   1959         assertEquals(5, Account.restoreAccountWithId(c, a5.mId).mNewMessageCount);
   1960 
   1961         // No ID in URI, with selection
   1962         cr.update(Account.RESET_NEW_MESSAGE_COUNT_URI, null,
   1963                 EmailContent.ID_SELECTION, new String[] {Long.toString(a2.mId)});
   1964         assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount);
   1965         assertEquals(0, Account.restoreAccountWithId(c, a2.mId).mNewMessageCount);
   1966         assertEquals(3, Account.restoreAccountWithId(c, a3.mId).mNewMessageCount);
   1967         assertEquals(4, Account.restoreAccountWithId(c, a4.mId).mNewMessageCount);
   1968         assertEquals(5, Account.restoreAccountWithId(c, a5.mId).mNewMessageCount);
   1969 
   1970         // With ID, with selection
   1971         cr.update(ContentUris.withAppendedId(Account.RESET_NEW_MESSAGE_COUNT_URI, a3.mId), null,
   1972                 EmailContent.ID_SELECTION, new String[] {Long.toString(a3.mId)});
   1973         assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount);
   1974         assertEquals(0, Account.restoreAccountWithId(c, a2.mId).mNewMessageCount);
   1975         assertEquals(0, Account.restoreAccountWithId(c, a3.mId).mNewMessageCount);
   1976         assertEquals(4, Account.restoreAccountWithId(c, a4.mId).mNewMessageCount);
   1977         assertEquals(5, Account.restoreAccountWithId(c, a5.mId).mNewMessageCount);
   1978 
   1979         // No ID in URI, no selection
   1980         cr.update(Account.RESET_NEW_MESSAGE_COUNT_URI, null, null, null);
   1981         assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount);
   1982         assertEquals(0, Account.restoreAccountWithId(c, a2.mId).mNewMessageCount);
   1983         assertEquals(0, Account.restoreAccountWithId(c, a3.mId).mNewMessageCount);
   1984         assertEquals(0, Account.restoreAccountWithId(c, a4.mId).mNewMessageCount);
   1985         assertEquals(0, Account.restoreAccountWithId(c, a5.mId).mNewMessageCount);
   1986     }
   1987 
   1988     /**
   1989      * Check if update on ACCOUNT_ID_ADD_TO_FIELD updates the cache properly.
   1990      */
   1991     public void testUpdateCacheAccountIdAddToField() {
   1992         final Context c = mMockContext;
   1993         Account a1 = ProviderTestUtils.setupAccount("a1", true, c);
   1994 
   1995         int start = Account.restoreAccountWithId(c, a1.mId).mNewMessageCount;
   1996 
   1997         // +1 to NEW_MESSAGE_COUNT
   1998         ContentValues cv = new ContentValues();
   1999         cv.put(EmailContent.FIELD_COLUMN_NAME, AccountColumns.NEW_MESSAGE_COUNT);
   2000         cv.put(EmailContent.ADD_COLUMN_NAME, 1);
   2001         mProvider.update(ContentUris.withAppendedId(Account.ADD_TO_FIELD_URI, a1.mId), cv,
   2002                 null, null);
   2003 
   2004         // Check
   2005         assertEquals(start + 1, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount);
   2006     }
   2007 
   2008     /**
   2009      * Check if update on ACCOUNT_RESET_NEW_COUNT updates the cache properly.
   2010      */
   2011     public void testUpdateCacheAccountResetNewCount() {
   2012         final Context c = mMockContext;
   2013         Account a1 = ProviderTestUtils.setupAccount("a1", true, c);
   2014 
   2015         // precondition
   2016         assertTrue(Account.restoreAccountWithId(c, a1.mId).mNewMessageCount > 0);
   2017 
   2018         // Reset
   2019         mProvider.update(Account.RESET_NEW_MESSAGE_COUNT_URI, null, null, null);
   2020 
   2021         // Check
   2022         assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount);
   2023     }
   2024 
   2025     /**
   2026      * Check if update on ACCOUNT_RESET_NEW_COUNT_ID updates the cache properly.
   2027      */
   2028     public void testUpdateCacheAccountResetNewCountId() {
   2029         final Context c = mMockContext;
   2030         Account a1 = ProviderTestUtils.setupAccount("a1", true, c);
   2031 
   2032         // precondition
   2033         assertTrue(Account.restoreAccountWithId(c, a1.mId).mNewMessageCount > 0);
   2034 
   2035         // Reset
   2036         mProvider.update(ContentUris.withAppendedId(Account.RESET_NEW_MESSAGE_COUNT_URI, a1.mId),
   2037                 null, null, null);
   2038 
   2039         // Check
   2040         assertEquals(0, Account.restoreAccountWithId(c, a1.mId).mNewMessageCount);
   2041     }
   2042 
   2043     /**
   2044      * Check that we're handling illegal uri's properly (by throwing an exception unless it's a
   2045      * query for an id of -1, in which case we return a zero-length cursor)
   2046      */
   2047     public void testIllegalUri() {
   2048         final ContentResolver cr = mMockContext.getContentResolver();
   2049 
   2050         ContentValues cv = new ContentValues();
   2051         Uri uri = Uri.parse("content://" + EmailContent.AUTHORITY + "/fooble");
   2052         try {
   2053             cr.insert(uri, cv);
   2054             fail("Insert should have thrown exception");
   2055         } catch (IllegalArgumentException e) {
   2056         }
   2057         try {
   2058             cr.update(uri, cv, null, null);
   2059             fail("Update should have thrown exception");
   2060         } catch (IllegalArgumentException e) {
   2061         }
   2062         try {
   2063             cr.delete(uri, null, null);
   2064             fail("Delete should have thrown exception");
   2065         } catch (IllegalArgumentException e) {
   2066         }
   2067         try {
   2068             cr.query(uri, EmailContent.ID_PROJECTION, null, null, null);
   2069             fail("Query should have thrown exception");
   2070         } catch (IllegalArgumentException e) {
   2071         }
   2072         uri = Uri.parse("content://" + EmailContent.AUTHORITY + "/mailbox/fred");
   2073         try {
   2074             cr.query(uri, EmailContent.ID_PROJECTION, null, null, null);
   2075             fail("Query should have thrown exception");
   2076         } catch (IllegalArgumentException e) {
   2077         }
   2078         uri = Uri.parse("content://" + EmailContent.AUTHORITY + "/mailbox/-1");
   2079         Cursor c = cr.query(uri, EmailContent.ID_PROJECTION, null, null, null);
   2080         assertNotNull(c);
   2081         assertEquals(0, c.getCount());
   2082         c.close();
   2083     }
   2084 
   2085     /**
   2086      * Verify {@link EmailProvider#recalculateMessageCount(android.database.sqlite.SQLiteDatabase)}
   2087      */
   2088     public void testRecalculateMessageCounts() {
   2089         final Context c = mMockContext;
   2090 
   2091         // Create accounts
   2092         Account a1 = ProviderTestUtils.setupAccount("holdflag-1", true, c);
   2093         Account a2 = ProviderTestUtils.setupAccount("holdflag-2", true, c);
   2094 
   2095         // Create mailboxes for each account
   2096         Mailbox b1 = ProviderTestUtils.setupMailbox("box1", a1.mId, true, c, Mailbox.TYPE_INBOX);
   2097         Mailbox b2 = ProviderTestUtils.setupMailbox("box2", a1.mId, true, c, Mailbox.TYPE_OUTBOX);
   2098         Mailbox b3 = ProviderTestUtils.setupMailbox("box3", a2.mId, true, c, Mailbox.TYPE_INBOX);
   2099         Mailbox b4 = ProviderTestUtils.setupMailbox("box4", a2.mId, true, c, Mailbox.TYPE_OUTBOX);
   2100         Mailbox bt = ProviderTestUtils.setupMailbox("boxT", a2.mId, true, c, Mailbox.TYPE_TRASH);
   2101 
   2102         // Create some messages
   2103         // b1 (account 1, inbox): 1 message, including 1 starred
   2104         Message m11 = createMessage(c, b1, true, false, Message.FLAG_LOADED_COMPLETE);
   2105 
   2106         // b2 (account 1, outbox): 2 message, including 1 starred
   2107         Message m21 = createMessage(c, b2, false, false, Message.FLAG_LOADED_COMPLETE);
   2108         Message m22 = createMessage(c, b2, true, true, Message.FLAG_LOADED_COMPLETE);
   2109 
   2110         // b3 (account 2, inbox): 3 message, including 1 starred
   2111         Message m31 = createMessage(c, b3, false, false, Message.FLAG_LOADED_COMPLETE);
   2112         Message m32 = createMessage(c, b3, false, false, Message.FLAG_LOADED_COMPLETE);
   2113         Message m33 = createMessage(c, b3, true, true, Message.FLAG_LOADED_COMPLETE);
   2114 
   2115         // b4 (account 2, outbox) has no messages.
   2116 
   2117         // bt (account 2, trash) has 3 messages, including 2 starred
   2118         Message mt1 = createMessage(c, bt, true, false, Message.FLAG_LOADED_COMPLETE);
   2119         Message mt2 = createMessage(c, bt, true, false, Message.FLAG_LOADED_COMPLETE);
   2120         Message mt3 = createMessage(c, bt, false, false, Message.FLAG_LOADED_COMPLETE);
   2121 
   2122         // Verifiy initial message counts
   2123         assertEquals(1, getMessageCount(b1.mId));
   2124         assertEquals(2, getMessageCount(b2.mId));
   2125         assertEquals(3, getMessageCount(b3.mId));
   2126         assertEquals(0, getMessageCount(b4.mId));
   2127         assertEquals(3, getMessageCount(bt.mId));
   2128 
   2129         // Whew. The setup is done; now let's actually get to the test
   2130 
   2131         // First, invalidate the message counts.
   2132         setMinusOneToMessageCounts();
   2133         assertEquals(-1, getMessageCount(b1.mId));
   2134         assertEquals(-1, getMessageCount(b2.mId));
   2135         assertEquals(-1, getMessageCount(b3.mId));
   2136         assertEquals(-1, getMessageCount(b4.mId));
   2137         assertEquals(-1, getMessageCount(bt.mId));
   2138 
   2139         // Batch update.
   2140         SQLiteDatabase db = getProvider().getDatabase(mMockContext);
   2141         EmailProvider.recalculateMessageCount(db);
   2142 
   2143         // Check message counts are valid again
   2144         assertEquals(1, getMessageCount(b1.mId));
   2145         assertEquals(2, getMessageCount(b2.mId));
   2146         assertEquals(3, getMessageCount(b3.mId));
   2147         assertEquals(0, getMessageCount(b4.mId));
   2148         assertEquals(3, getMessageCount(bt.mId));
   2149     }
   2150 
   2151     /** Creates an account */
   2152     private Account createAccount(Context c, String name, HostAuth recvAuth, HostAuth sendAuth) {
   2153         Account account = ProviderTestUtils.setupAccount(name, false, c);
   2154         if (recvAuth != null) {
   2155             account.mHostAuthKeyRecv = recvAuth.mId;
   2156             if (sendAuth == null) {
   2157                 account.mHostAuthKeySend = recvAuth.mId;
   2158             }
   2159         }
   2160         if (sendAuth != null) {
   2161             account.mHostAuthKeySend = sendAuth.mId;
   2162         }
   2163         account.save(c);
   2164         return account;
   2165     }
   2166 
   2167     /** Creates a mailbox; redefine as we need version 17 mailbox values */
   2168     private Mailbox createMailbox(Context c, String displayName, String serverId, long parentKey,
   2169             long accountId) {
   2170         Mailbox box = new Mailbox();
   2171 
   2172         box.mDisplayName = displayName;
   2173         box.mServerId = serverId;
   2174         box.mParentKey = parentKey;
   2175         box.mAccountKey = accountId;
   2176         // Don't care about the fields below ... set them for giggles
   2177         box.mType = Mailbox.TYPE_MAIL;
   2178         box.mDelimiter = '/';
   2179         box.mSyncKey = "sync-key";
   2180         box.mSyncLookback = 2;
   2181         box.mSyncInterval = Account.CHECK_INTERVAL_NEVER;
   2182         box.mSyncTime = 3;
   2183         box.mFlagVisible = true;
   2184         box.mFlags = 5;
   2185         box.mVisibleLimit = 6;
   2186         box.save(c);
   2187         return box;
   2188     }
   2189 
   2190     /**
   2191      * Asserts equality between two mailboxes. We define this as we don't have implementations
   2192      * for Mailbox#equals().
   2193      */
   2194     private void assertEquals(Mailbox expected, Mailbox actual) {
   2195         if (expected == null && actual == null) return;
   2196         assertTrue(expected != null && actual != null);
   2197         assertEqualsExceptServerId(expected, actual, expected.mServerId);
   2198     }
   2199 
   2200     /**
   2201      * Asserts equality between the two mailboxes EXCEPT for the server id. The given server
   2202      * ID is the expected value.
   2203      */
   2204     private void assertEqualsExceptServerId(Mailbox expected, Mailbox actual, String serverId) {
   2205         if (expected == null && actual == null) return;
   2206 
   2207         assertTrue(expected != null && actual != null);
   2208         assertEquals(expected.mDisplayName, actual.mDisplayName);
   2209         assertEquals(serverId, actual.mServerId);
   2210         assertEquals(expected.mParentKey, actual.mParentKey);
   2211         assertEquals(expected.mAccountKey, actual.mAccountKey);
   2212     }
   2213 
   2214     /** Verifies updating the DB from v17 to v18 works as expected */
   2215     public void testUpgradeFromVersion17ToVersion18() {
   2216         final Context c = mMockContext;
   2217         // Create accounts
   2218         Account a1 = createAccount(c, "exchange",
   2219                 ProviderTestUtils.setupHostAuth("eas", "exchange.host.com", true, c),
   2220                 null);
   2221         Account a2 = createAccount(c, "imap",
   2222                 ProviderTestUtils.setupHostAuth("imap", "imap.host.com", true, c),
   2223                 ProviderTestUtils.setupHostAuth("smtp", "smtp.host.com", true, c));
   2224         Account a3 = createAccount(c, "pop3",
   2225                 ProviderTestUtils.setupHostAuth("pop3", "imap.host.com", true, c),
   2226                 ProviderTestUtils.setupHostAuth("smtp", "smtp.host.com", true, c));
   2227 
   2228         // Create mailboxes; some w/ valid parent IDs, others without
   2229         Mailbox b11 = createMailbox(c, "box1", "12", 0L, a1.mId);
   2230         Mailbox b12 = createMailbox(c, "box2", "67", -1L, a1.mId);
   2231         Mailbox b13 = createMailbox(c, "box3", "18", b12.mId, a1.mId);
   2232 
   2233         Mailbox b21 = createMailbox(c, "box4", null, 0L, a2.mId);
   2234         Mailbox b22 = createMailbox(c, "box4/foo/bar", "will-be-replaced", 0L, a2.mId);
   2235         Mailbox b23 = createMailbox(c, "box5", null, -1L, a2.mId);
   2236         Mailbox b24 = createMailbox(c, "box6", "box5/box6", b23.mId, a2.mId);
   2237 
   2238         Mailbox b31 = createMailbox(c, "box7", "12", 0L, a3.mId);
   2239         Mailbox b32 = createMailbox(c, "box8/foo/bar", "will-be-replaced", 0L, a3.mId);
   2240         Mailbox b33 = createMailbox(c, "box9", "box9", -1L, a3.mId);
   2241         Mailbox b34 = createMailbox(c, "boxA", "box9/boxA", b33.mId, a3.mId);
   2242 
   2243         // Sanity check the mailboxes that were just added
   2244         Mailbox testMailbox;
   2245         testMailbox = Mailbox.restoreMailboxWithId(c, b11.mId);
   2246         assertEquals(b11, testMailbox);
   2247         testMailbox = Mailbox.restoreMailboxWithId(c, b12.mId);
   2248         assertEquals(b12, testMailbox);
   2249         testMailbox = Mailbox.restoreMailboxWithId(c, b13.mId);
   2250         assertEquals(b13, testMailbox);
   2251         testMailbox = Mailbox.restoreMailboxWithId(c, b21.mId);
   2252         assertEqualsExceptServerId(b21, testMailbox, null);
   2253         testMailbox = Mailbox.restoreMailboxWithId(c, b22.mId);
   2254         assertEqualsExceptServerId(b22, testMailbox, "will-be-replaced");
   2255         testMailbox = Mailbox.restoreMailboxWithId(c, b23.mId);
   2256         assertEquals(b23, testMailbox);
   2257         testMailbox = Mailbox.restoreMailboxWithId(c, b24.mId);
   2258         assertEquals(b24, testMailbox);
   2259         testMailbox = Mailbox.restoreMailboxWithId(c, b31.mId);
   2260         assertEqualsExceptServerId(b31, testMailbox, "12");
   2261         testMailbox = Mailbox.restoreMailboxWithId(c, b32.mId);
   2262         assertEqualsExceptServerId(b32, testMailbox, "will-be-replaced");
   2263         testMailbox = Mailbox.restoreMailboxWithId(c, b33.mId);
   2264         assertEquals(b33, testMailbox);
   2265         testMailbox = Mailbox.restoreMailboxWithId(c, b34.mId);
   2266         assertEquals(b34, testMailbox);
   2267 
   2268         SQLiteDatabase db = getProvider().getDatabase(mMockContext);
   2269         EmailProvider.upgradeFromVersion17ToVersion18(db);
   2270 
   2271         // Verify that only IMAP/POP3 mailboxes w/ a parent key of '0' are changed
   2272         // Exchange mailboxes; none should be changed
   2273         testMailbox = Mailbox.restoreMailboxWithId(c, b11.mId);
   2274         assertEquals(b11, testMailbox);
   2275         testMailbox = Mailbox.restoreMailboxWithId(c, b12.mId);
   2276         assertEquals(b12, testMailbox);
   2277         testMailbox = Mailbox.restoreMailboxWithId(c, b13.mId);
   2278         assertEquals(b13, testMailbox);
   2279 
   2280         // IMAP mailboxes; only mailboxes w/ a parent id of '0' are changed
   2281         testMailbox = Mailbox.restoreMailboxWithId(c, b21.mId);
   2282         assertEqualsExceptServerId(b21, testMailbox, "box4");
   2283         testMailbox = Mailbox.restoreMailboxWithId(c, b22.mId);
   2284         assertEqualsExceptServerId(b22, testMailbox, "box4/foo/bar");
   2285         testMailbox = Mailbox.restoreMailboxWithId(c, b23.mId);
   2286         assertEquals(b23, testMailbox);
   2287         testMailbox = Mailbox.restoreMailboxWithId(c, b24.mId);
   2288         assertEquals(b24, testMailbox);
   2289 
   2290         // POP3 mailboxes; only mailboxes w/ a parent id of '0' are changed
   2291         testMailbox = Mailbox.restoreMailboxWithId(c, b31.mId);
   2292         assertEqualsExceptServerId(b31, testMailbox, "box7");
   2293         testMailbox = Mailbox.restoreMailboxWithId(c, b32.mId);
   2294         assertEqualsExceptServerId(b32, testMailbox, "box8/foo/bar");
   2295         testMailbox = Mailbox.restoreMailboxWithId(c, b33.mId);
   2296         assertEquals(b33, testMailbox);
   2297         testMailbox = Mailbox.restoreMailboxWithId(c, b34.mId);
   2298         assertEquals(b34, testMailbox);
   2299     }
   2300 
   2301     /**
   2302      * Determine whether a list of AccountManager accounts includes a given EmailProvider account
   2303      * @param amAccountList a list of AccountManager accounts
   2304      * @param account an EmailProvider account
   2305      * @param context the caller's context (our test provider's context)
   2306      * @return whether or not the EmailProvider account is represented in AccountManager
   2307      */
   2308     private boolean amAccountListHasAccount(android.accounts.Account[] amAccountList,
   2309             Account account, Context context) {
   2310         String email = account.mEmailAddress;
   2311         for (android.accounts.Account amAccount: amAccountList) {
   2312             if (amAccount.name.equals(email)) {
   2313                 return true;
   2314             }
   2315         }
   2316         return false;
   2317     }
   2318 
   2319     public void testAutoCacheNewContent() {
   2320         Account account = ProviderTestUtils.setupAccount("account-hostauth", false, mMockContext);
   2321         // add hostauth data, which should be saved the first time
   2322         account.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-hostauth-recv", -1, false,
   2323                 mMockContext);
   2324         account.mHostAuthSend = ProviderTestUtils.setupHostAuth("account-hostauth-send", -1, false,
   2325                 mMockContext);
   2326         account.save(mMockContext);
   2327         assertTrue(mProvider.isCached(Account.CONTENT_URI, account.mId));
   2328         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, account.mHostAuthRecv.mId));
   2329         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, account.mHostAuthSend.mId));
   2330     }
   2331 
   2332     /** Creates a mailbox; redefine as we need version 17 mailbox values */
   2333     private Mailbox createTypeMailbox(Context c, long accountId, int type) {
   2334         Mailbox box = new Mailbox();
   2335 
   2336         box.mDisplayName = "foo";
   2337         box.mServerId = "1:1";
   2338         box.mParentKey = 0;
   2339         box.mAccountKey = accountId;
   2340         // Don't care about the fields below ... set them for giggles
   2341         box.mType = type;
   2342         box.save(c);
   2343         return box;
   2344     }
   2345 
   2346     public void testAutoCacheInvalidate() {
   2347         // Create 3 accounts with hostauth and 3 mailboxes each (2 of which are pre-cached)
   2348         Account a = ProviderTestUtils.setupAccount("account1", false, mMockContext);
   2349         a.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-recv", -1, false,
   2350                 mMockContext);
   2351         a.mHostAuthSend = ProviderTestUtils.setupHostAuth("account-send", -1, false,
   2352                 mMockContext);
   2353         a.save(mMockContext);
   2354         Mailbox a1 = createTypeMailbox(mMockContext, a.mId, Mailbox.TYPE_INBOX);
   2355         Mailbox a2 = createTypeMailbox(mMockContext, a.mId, Mailbox.TYPE_MAIL);
   2356         Mailbox a3 = createTypeMailbox(mMockContext, a.mId, Mailbox.TYPE_DRAFTS);
   2357         Account b = ProviderTestUtils.setupAccount("account2", false, mMockContext);
   2358         b.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-recv", -1, false,
   2359                 mMockContext);
   2360         b.mHostAuthSend = ProviderTestUtils.setupHostAuth("accoun-send", -1, false,
   2361                 mMockContext);
   2362         b.save(mMockContext);
   2363         Mailbox b1 = createTypeMailbox(mMockContext, b.mId, Mailbox.TYPE_OUTBOX);
   2364         Mailbox b2 = createTypeMailbox(mMockContext, b.mId, Mailbox.TYPE_MAIL);
   2365         Mailbox b3 = createTypeMailbox(mMockContext, b.mId, Mailbox.TYPE_SENT);
   2366         Account c = ProviderTestUtils.setupAccount("account3", false, mMockContext);
   2367         c.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-recv", -1, false,
   2368                 mMockContext);
   2369         c.mHostAuthSend = ProviderTestUtils.setupHostAuth("account-send", -1, false,
   2370                 mMockContext);
   2371         c.save(mMockContext);
   2372         Mailbox c1 = createTypeMailbox(mMockContext, c.mId, Mailbox.TYPE_SEARCH);
   2373         Mailbox c2 = createTypeMailbox(mMockContext, c.mId, Mailbox.TYPE_MAIL);
   2374         Mailbox c3 = createTypeMailbox(mMockContext, c.mId, Mailbox.TYPE_TRASH);
   2375 
   2376         // Confirm expected cache state
   2377         assertTrue(mProvider.isCached(Account.CONTENT_URI, a.mId));
   2378         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, a.mHostAuthRecv.mId));
   2379         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, a.mHostAuthSend.mId));
   2380         assertTrue(mProvider.isCached(Account.CONTENT_URI, b.mId));
   2381         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, b.mHostAuthRecv.mId));
   2382         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, b.mHostAuthSend.mId));
   2383         assertTrue(mProvider.isCached(Account.CONTENT_URI, c.mId));
   2384         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, c.mHostAuthRecv.mId));
   2385         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, c.mHostAuthSend.mId));
   2386 
   2387         assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, a1.mId));
   2388         assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, a2.mId));
   2389         assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, a3.mId));
   2390         assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, b1.mId));
   2391         assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, b2.mId));
   2392         assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, b3.mId));
   2393         assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, c1.mId));
   2394         assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, c2.mId));
   2395         assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, c3.mId));
   2396 
   2397         // Delete account b
   2398         EmailContent.delete(mMockContext, Account.CONTENT_URI, b.mId);
   2399 
   2400         // Confirm cache state
   2401         assertTrue(mProvider.isCached(Account.CONTENT_URI, a.mId));
   2402         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, a.mHostAuthRecv.mId));
   2403         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, a.mHostAuthSend.mId));
   2404         assertFalse(mProvider.isCached(Account.CONTENT_URI, b.mId));
   2405         assertFalse(mProvider.isCached(HostAuth.CONTENT_URI, b.mHostAuthRecv.mId));
   2406         assertFalse(mProvider.isCached(HostAuth.CONTENT_URI, b.mHostAuthSend.mId));
   2407         assertTrue(mProvider.isCached(Account.CONTENT_URI, c.mId));
   2408         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, c.mHostAuthRecv.mId));
   2409         assertTrue(mProvider.isCached(HostAuth.CONTENT_URI, c.mHostAuthSend.mId));
   2410 
   2411         assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, a1.mId));
   2412         assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, a2.mId));
   2413         assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, a3.mId));
   2414         assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, b1.mId));
   2415         assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, b2.mId));
   2416         assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, b3.mId));
   2417         assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, c1.mId));
   2418         assertFalse(mProvider.isCached(Mailbox.CONTENT_URI, c2.mId));
   2419         assertTrue(mProvider.isCached(Mailbox.CONTENT_URI, c3.mId));
   2420     }
   2421 
   2422     /**
   2423      * Remove a single pop/imap account from the AccountManager
   2424      * @param accountManager our AccountManager
   2425      * @param name the name of the test account to remove
   2426      */
   2427     private void removeAccountManagerAccount(AccountManager accountManager, String name) {
   2428         try {
   2429             accountManager.removeAccount(
   2430                     new android.accounts.Account(name, AccountManagerTypes.TYPE_POP_IMAP),
   2431                     null, null).getResult();
   2432         } catch (OperationCanceledException e) {
   2433         } catch (AuthenticatorException e) {
   2434         } catch (IOException e) {
   2435         }
   2436     }
   2437 
   2438     /**
   2439      * Remove all test accounts from the AccountManager
   2440      * @param accountManager the AccountManager
   2441      */
   2442     private void cleanupTestAccountManagerAccounts(AccountManager accountManager) {
   2443         android.accounts.Account[] amAccountList =
   2444             accountManager.getAccountsByType(AccountManagerTypes.TYPE_POP_IMAP);
   2445         for (android.accounts.Account account: amAccountList) {
   2446             if (account.name.startsWith(AccountReconciler.ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX)) {
   2447                 removeAccountManagerAccount(accountManager, account.name);
   2448             }
   2449         }
   2450     }
   2451 
   2452     /** Verifies updating the DB from v21 to v22 works as expected */
   2453     public void testUpgradeFromVersion21ToVersion22() {
   2454         String imapTestLogin =
   2455             AccountReconciler.ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX + "imap.host.com";
   2456         String pop3TestLogin =
   2457             AccountReconciler.ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX + "pop3.host.com";
   2458         AccountManager accountManager = AccountManager.get(mContext);
   2459 
   2460         // Create provider accounts (one of each type)
   2461         Account a1 = createAccount(mMockContext, "exchange",
   2462                 ProviderTestUtils.setupHostAuth("eas", "exchange.host.com", true, mMockContext),
   2463                 null);
   2464         HostAuth h2 =
   2465             ProviderTestUtils.setupHostAuth("imap", "imap.host.com", false, mMockContext);
   2466         h2.mLogin = imapTestLogin;
   2467         h2.save(mMockContext);
   2468         Account a2 = createAccount(mMockContext, "imap", h2,
   2469                 ProviderTestUtils.setupHostAuth("smtp", "smtp.host.com", true, mMockContext));
   2470         HostAuth h3 =
   2471             ProviderTestUtils.setupHostAuth("pop3", "pop3.host.com", false, mMockContext);
   2472         h3.mLogin = pop3TestLogin;
   2473         h3.save(mMockContext);
   2474         Account a3 = createAccount(mMockContext, "pop3", h3,
   2475                 ProviderTestUtils.setupHostAuth("smtp", "smtp.host.com", true, mMockContext));
   2476 
   2477         // Get the current list of AccountManager accounts (we have to use the real context here),
   2478         // whereas we use the mock context for EmailProvider (this is because the mock context
   2479         // doesn't implement AccountManager hooks)
   2480         android.accounts.Account[] amAccountList =
   2481             accountManager.getAccountsByType(AccountManagerTypes.TYPE_POP_IMAP);
   2482         // There shouldn't be AccountManager accounts for these
   2483         assertFalse(amAccountListHasAccount(amAccountList, a1, mMockContext));
   2484         assertFalse(amAccountListHasAccount(amAccountList, a2, mMockContext));
   2485         assertFalse(amAccountListHasAccount(amAccountList, a3, mMockContext));
   2486 
   2487         amAccountList = null;
   2488         try {
   2489             // Upgrade the database
   2490             SQLiteDatabase db = getProvider().getDatabase(mMockContext);
   2491             EmailProvider.upgradeFromVersion21ToVersion22(db, getContext());
   2492 
   2493             // The pop3 and imap account should now be in account manager
   2494             amAccountList = accountManager.getAccountsByType(AccountManagerTypes.TYPE_POP_IMAP);
   2495             assertFalse(amAccountListHasAccount(amAccountList, a1, mMockContext));
   2496             assertTrue(amAccountListHasAccount(amAccountList, a2, mMockContext));
   2497             assertTrue(amAccountListHasAccount(amAccountList, a3, mMockContext));
   2498         } finally {
   2499             cleanupTestAccountManagerAccounts(accountManager);
   2500         }
   2501     }
   2502 
   2503     public void testCleanupOrphans() {
   2504         EmailProvider ep = getProvider();
   2505         SQLiteDatabase db = ep.getDatabase(mMockContext);
   2506 
   2507         Account a = ProviderTestUtils.setupAccount("account1", true, mMockContext);
   2508         // Mailbox a1 and a3 won't have a valid account
   2509         Mailbox a1 = createTypeMailbox(mMockContext, -1, Mailbox.TYPE_INBOX);
   2510         Mailbox a2 = createTypeMailbox(mMockContext, a.mId, Mailbox.TYPE_MAIL);
   2511         Mailbox a3 = createTypeMailbox(mMockContext, -1, Mailbox.TYPE_DRAFTS);
   2512         Mailbox a4 = createTypeMailbox(mMockContext, a.mId, Mailbox.TYPE_SENT);
   2513         Mailbox a5 = createTypeMailbox(mMockContext, a.mId, Mailbox.TYPE_TRASH);
   2514         // Mailbox ax isn't even saved; use an obviously invalid id
   2515         Mailbox ax = new Mailbox();
   2516         ax.mId = 69105;
   2517 
   2518         // Message mt2 is an orphan, as is mt4
   2519         Message m1 = createMessage(mMockContext, a1, true, false, Message.FLAG_LOADED_COMPLETE);
   2520         Message m2 = createMessage(mMockContext, a2, true, false, Message.FLAG_LOADED_COMPLETE);
   2521         Message m3 = createMessage(mMockContext, a3, true, false, Message.FLAG_LOADED_COMPLETE);
   2522         Message m4 = createMessage(mMockContext, a4, true, false, Message.FLAG_LOADED_COMPLETE);
   2523         Message m5 = createMessage(mMockContext, a5, true, false, Message.FLAG_LOADED_COMPLETE);
   2524         Message mx = createMessage(mMockContext, ax, true, false, Message.FLAG_LOADED_COMPLETE);
   2525 
   2526         // Two orphan policies
   2527         Policy p1 = new Policy();
   2528         p1.save(mMockContext);
   2529         Policy p2 = new Policy();
   2530         p2.save(mMockContext);
   2531         Policy p3 = new Policy();
   2532         Policy.setAccountPolicy(mMockContext, a.mId, p3, "0");
   2533 
   2534         // We don't want anything cached or the tests below won't work.  Note that
   2535         // deleteUnlinked is only called by EmailProvider when the caches are empty
   2536         ContentCache.invalidateAllCaches();
   2537         // Delete orphaned mailboxes/messages/policies
   2538         ep.deleteUnlinked(db, Mailbox.TABLE_NAME, MailboxColumns.ACCOUNT_KEY, AccountColumns.ID,
   2539                 Account.TABLE_NAME);
   2540         ep.deleteUnlinked(db, Message.TABLE_NAME, MessageColumns.ACCOUNT_KEY, AccountColumns.ID,
   2541                 Account.TABLE_NAME);
   2542         ep.deleteUnlinked(db, Policy.TABLE_NAME, PolicyColumns.ID, AccountColumns.POLICY_KEY,
   2543                 Account.TABLE_NAME);
   2544 
   2545         // Make sure the orphaned mailboxes are gone
   2546         assertNull(Mailbox.restoreMailboxWithId(mMockContext, a1.mId));
   2547         assertNotNull(Mailbox.restoreMailboxWithId(mMockContext, a2.mId));
   2548         assertNull(Mailbox.restoreMailboxWithId(mMockContext, a3.mId));
   2549         assertNotNull(Mailbox.restoreMailboxWithId(mMockContext, a4.mId));
   2550         assertNotNull(Mailbox.restoreMailboxWithId(mMockContext, a5.mId));
   2551         assertNull(Mailbox.restoreMailboxWithId(mMockContext, ax.mId));
   2552 
   2553         // Make sure orphaned messages are gone
   2554         assertNull(Message.restoreMessageWithId(mMockContext, m1.mId));
   2555         assertNotNull(Message.restoreMessageWithId(mMockContext, m2.mId));
   2556         assertNull(Message.restoreMessageWithId(mMockContext, m3.mId));
   2557         assertNotNull(Message.restoreMessageWithId(mMockContext, m4.mId));
   2558         assertNotNull(Message.restoreMessageWithId(mMockContext, m5.mId));
   2559         assertNull(Message.restoreMessageWithId(mMockContext, mx.mId));
   2560 
   2561         // Make sure orphaned policies are gone
   2562         assertNull(Policy.restorePolicyWithId(mMockContext, p1.mId));
   2563         assertNull(Policy.restorePolicyWithId(mMockContext, p2.mId));
   2564         a = Account.restoreAccountWithId(mMockContext, a.mId);
   2565         assertNotNull(Policy.restorePolicyWithId(mMockContext, a.mPolicyKey));
   2566     }
   2567 }
   2568