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