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