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 android.content.ContentResolver;
20import android.content.Context;
21import android.content.res.AssetFileDescriptor;
22import android.database.Cursor;
23import android.graphics.Bitmap;
24import android.graphics.BitmapFactory;
25import android.net.Uri;
26import android.test.ProviderTestCase2;
27import android.test.mock.MockContentResolver;
28import android.test.suitebuilder.annotation.Suppress;
29
30import com.android.email.AttachmentInfo;
31import com.android.email.R;
32import com.android.emailcommon.mail.MessagingException;
33import com.android.emailcommon.provider.Account;
34import com.android.emailcommon.provider.EmailContent;
35import com.android.emailcommon.provider.EmailContent.Attachment;
36import com.android.emailcommon.provider.EmailContent.Message;
37import com.android.emailcommon.provider.Mailbox;
38import com.android.emailcommon.utility.AttachmentUtilities;
39
40import java.io.File;
41import java.io.FileNotFoundException;
42import java.io.FileOutputStream;
43import java.io.IOException;
44
45/**
46 * Tests of the Email Attachments provider.
47 *
48 * You can run this entire test case with:
49 *   runtest -c com.android.email.provider.AttachmentProviderTests email
50 */
51@Suppress
52public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvider> {
53
54    EmailProvider mEmailProvider;
55    Context mMockContext;
56    ContentResolver mMockResolver;
57
58    public AttachmentProviderTests() {
59        super(AttachmentProvider.class, Attachment.ATTACHMENT_PROVIDER_LEGACY_URI_PREFIX);
60    }
61
62    @Override
63    public void setUp() throws Exception {
64        super.setUp();
65        mMockContext = getMockContext();
66        mMockResolver = mMockContext.getContentResolver();
67
68        // Spin up an Email provider as well and put it under the same mock test framework
69        mEmailProvider = new EmailProvider();
70        mEmailProvider.attachInfo(mMockContext, null);
71        assertNotNull(mEmailProvider);
72        ((MockContentResolver) mMockResolver)
73                .addProvider(EmailContent.AUTHORITY, mEmailProvider);
74    }
75
76    /**
77     * test query()
78     *  - item found
79     *  - item not found
80     *  - permuted projection
81     */
82    public void testQuery() throws MessagingException {
83        Account account1 = ProviderTestUtils.setupAccount("attachment-query", false, mMockContext);
84        account1.save(mMockContext);
85        final long message1Id = 1;
86        long attachment1Id = 1;
87        long attachment2Id = 2;
88        long attachment3Id = 3;
89
90        // Note:  There is an implicit assumption in this test sequence that the first
91        // attachment we add will be id=1 and the 2nd will have id=2.  This could fail on
92        // a legitimate implementation.  Asserts below will catch this and fail the test
93        // if necessary.
94        Uri attachment1Uri = AttachmentUtilities.getAttachmentUri(account1.mId,
95                attachment1Id);
96        Uri attachment2Uri = AttachmentUtilities.getAttachmentUri(account1.mId,
97                attachment2Id);
98        Uri attachment3Uri = AttachmentUtilities.getAttachmentUri(account1.mId,
99                attachment3Id);
100
101        // Test with no attachment found - should return null
102        Cursor c = mMockResolver.query(attachment1Uri, (String[])null, null, (String[])null, null);
103        assertNull(c);
104
105        // Add a couple of attachment entries.  Note, query() just uses the DB, and does not
106        // sample the files, so we won't bother creating the files
107        Attachment newAttachment1 = ProviderTestUtils.setupAttachment(message1Id, "file1", 100,
108                false, mMockContext);
109        newAttachment1.setContentUri(
110            AttachmentUtilities.getAttachmentUri(account1.mId, attachment1Id).toString());
111        attachment1Id = addAttachmentToDb(account1, newAttachment1);
112        assertEquals("Broken test:  Unexpected id assignment", 1, attachment1Id);
113
114        Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1Id, "file2", 200,
115                false, mMockContext);
116        newAttachment2.setContentUri(
117            AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id).toString());
118        attachment2Id = addAttachmentToDb(account1, newAttachment2);
119        assertEquals("Broken test:  Unexpected id assignment", 2, attachment2Id);
120
121        Attachment newAttachment3 = ProviderTestUtils.setupAttachment(message1Id, "file3", 300,
122                false, mMockContext);
123        newAttachment3.setContentUri(
124            AttachmentUtilities.getAttachmentUri(account1.mId, attachment3Id).toString());
125        attachment3Id = addAttachmentToDb(account1, newAttachment3);
126        assertEquals("Broken test:  Unexpected id assignment", 3, attachment3Id);
127
128        // Return a row with all columns specified
129        attachment2Uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id);
130        c = mMockResolver.query(
131                attachment2Uri,
132                new String[] { AttachmentUtilities.Columns._ID,
133                               AttachmentUtilities.Columns.DATA,
134                               AttachmentUtilities.Columns.DISPLAY_NAME,
135                               AttachmentUtilities.Columns.SIZE },
136                null, null, null);
137        assertEquals(1, c.getCount());
138        assertTrue(c.moveToFirst());
139        assertEquals(attachment2Id, c.getLong(0));                  // id
140        assertEquals(attachment2Uri.toString(), c.getString(1));    // content URI
141        assertEquals("file2", c.getString(2));                      // display name
142        assertEquals(200, c.getInt(3));                             // size
143
144        // Return a row with permuted columns
145        attachment3Uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment3Id);
146        c = mMockResolver.query(
147                attachment3Uri,
148                new String[] { AttachmentUtilities.Columns.SIZE,
149                               AttachmentUtilities.Columns.DISPLAY_NAME,
150                               AttachmentUtilities.Columns.DATA,
151                               AttachmentUtilities.Columns._ID },
152                null, null, null);
153        assertEquals(1, c.getCount());
154        assertTrue(c.moveToFirst());
155        assertEquals(attachment3Id, c.getLong(3));                  // id
156        assertEquals(attachment3Uri.toString(), c.getString(2));    // content URI
157        assertEquals("file3", c.getString(1));                      // display name
158        assertEquals(300, c.getInt(0));                             // size
159    }
160
161    private static Message createMessage(Context c, Mailbox b) {
162        Message m = ProviderTestUtils.setupMessage("1", b.mAccountKey, b.mId, true, false, c, false,
163                false);
164        m.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
165        m.save(c);
166        return m;
167    }
168
169    public void testInboxQuery() {
170        // Create 2 accounts
171        Account a1 = ProviderTestUtils.setupAccount("inboxquery-1", true, mMockContext);
172        Account a2 = ProviderTestUtils.setupAccount("inboxquery-2", true, mMockContext);
173
174        // Create mailboxes for each account
175        Mailbox b1 = ProviderTestUtils.setupMailbox(
176                "box1", a1.mId, true, mMockContext, Mailbox.TYPE_INBOX);
177        Mailbox b2 = ProviderTestUtils.setupMailbox(
178                "box2", a1.mId, true, mMockContext, Mailbox.TYPE_MAIL);
179        Mailbox b3 = ProviderTestUtils.setupMailbox(
180                "box3", a2.mId, true, mMockContext, Mailbox.TYPE_INBOX);
181        Mailbox b4 = ProviderTestUtils.setupMailbox(
182                "box4", a2.mId, true, mMockContext, Mailbox.TYPE_MAIL);
183        Mailbox bt = ProviderTestUtils.setupMailbox(
184                "boxT", a2.mId, true, mMockContext, Mailbox.TYPE_TRASH);
185
186        // Create some messages
187        // b1 (account 1, inbox): 2 messages
188        Message m11 = createMessage(mMockContext, b1);
189        Message m12 = createMessage(mMockContext, b1);
190
191        // b2 (account 1, mail): 2 messages
192        Message m21 = createMessage(mMockContext, b2);
193        Message m22 = createMessage(mMockContext, b2);
194
195        // b3 (account 2, inbox): 1 message
196        Message m31 = createMessage(mMockContext, b3);
197
198        // b4 (account 2, mail) has no messages.
199
200        // bt (account 2, trash): 1 message
201        Message mt1 = createMessage(mMockContext, bt);
202
203        // 4 attachments in the inbox, 2 different messages, 1 downloaded
204        createAttachment(a1, m11.mId, null);
205        createAttachment(a1, m11.mId, null);
206        createAttachment(a1, m12.mId, null);
207        createAttachment(a1, m12.mId, "file:///path/to/file1");
208
209        // 3 attachments in generic mailbox, 2 different messages, 1 downloaded
210        createAttachment(a1, m21.mId, null);
211        createAttachment(a1, m21.mId, null);
212        createAttachment(a1, m22.mId, null);
213        createAttachment(a1, m22.mId, "file:///path/to/file2");
214
215        // 1 attachment in inbox
216        createAttachment(a2, m31.mId, null);
217
218        // 2 attachments in trash, same message
219        createAttachment(a2, mt1.mId, null);
220        createAttachment(a2, mt1.mId, null);
221
222        Cursor c = null;
223        try {
224            // count all attachments with an empty URI, regardless of mailbox location
225            c = mMockContext.getContentResolver().query(
226                    Attachment.CONTENT_URI, AttachmentInfo.PROJECTION,
227                    EmailContent.Attachment.PRECACHE_SELECTION,
228                    null, Attachment.RECORD_ID + " DESC");
229            assertEquals(9, c.getCount());
230        } finally {
231            c.close();
232        }
233
234        try {
235            // count all attachments with an empty URI, only in an inbox
236            c = mMockContext.getContentResolver().query(
237                    Attachment.CONTENT_URI, AttachmentInfo.PROJECTION,
238                    EmailContent.Attachment.PRECACHE_INBOX_SELECTION,
239                    null, Attachment.RECORD_ID + " DESC");
240            assertEquals(4, c.getCount());
241        } finally {
242            c.close();
243        }
244    }
245
246    /**
247     * test getType()
248     *  - regular file
249     *  - thumbnail
250     */
251    public void testGetType() throws MessagingException {
252        Account account1 = ProviderTestUtils.setupAccount("get-type", false, mMockContext);
253        account1.save(mMockContext);
254        final long message1Id = 1;
255        long attachment1Id = 1;
256        long attachment2Id = 2;
257        long attachment3Id = 3;
258        long attachment4Id = 4;
259        long attachment5Id = 5;
260        long attachment6Id = 6;
261
262        Uri attachment1Uri = AttachmentUtilities.getAttachmentUri(account1.mId,
263                attachment1Id);
264
265        // Test with no attachment found - should return null
266        String type = mMockResolver.getType(attachment1Uri);
267        assertNull(type);
268
269        // Add a couple of attachment entries.  Note, getType() just uses the DB, and does not
270        // sample the files, so we won't bother creating the files
271        Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1Id, "file2", 100,
272                false, mMockContext);
273        newAttachment2.mMimeType = "image/jpg";
274        attachment2Id = addAttachmentToDb(account1, newAttachment2);
275
276        Attachment newAttachment3 = ProviderTestUtils.setupAttachment(message1Id, "file3", 100,
277                false, mMockContext);
278        newAttachment3.mMimeType = "text/plain";
279        attachment3Id = addAttachmentToDb(account1, newAttachment3);
280
281        Attachment newAttachment4 = ProviderTestUtils.setupAttachment(message1Id, "file4.doc", 100,
282                false, mMockContext);
283        newAttachment4.mMimeType = "application/octet-stream";
284        attachment4Id = addAttachmentToDb(account1, newAttachment4);
285
286        Attachment newAttachment5 = ProviderTestUtils.setupAttachment(message1Id, "file5.xyz", 100,
287                false, mMockContext);
288        newAttachment5.mMimeType = "application/octet-stream";
289        attachment5Id = addAttachmentToDb(account1, newAttachment5);
290
291        Attachment newAttachment6 = ProviderTestUtils.setupAttachment(message1Id, "file6", 100,
292                false, mMockContext);
293        newAttachment6.mMimeType = "";
294        attachment6Id = addAttachmentToDb(account1, newAttachment6);
295
296        // Check the returned filetypes
297        Uri uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id);
298        type = mMockResolver.getType(uri);
299        assertEquals("image/jpg", type);
300        uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment3Id);
301        type = mMockResolver.getType(uri);
302        assertEquals("text/plain", type);
303        uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment4Id);
304        type = mMockResolver.getType(uri);
305        assertEquals("application/msword", type);
306        uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment5Id);
307        type = mMockResolver.getType(uri);
308        assertEquals("application/xyz", type);
309        uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment6Id);
310        type = mMockResolver.getType(uri);
311        assertEquals("application/octet-stream", type);
312
313        // Check the returned filetypes for the thumbnails
314        uri = AttachmentUtilities.getAttachmentThumbnailUri(account1.mId, attachment2Id, 62,
315                62);
316        type = mMockResolver.getType(uri);
317        assertEquals("image/png", type);
318        uri = AttachmentUtilities.getAttachmentThumbnailUri(account1.mId, attachment3Id, 62,
319                62);
320        type = mMockResolver.getType(uri);
321        assertEquals("image/png", type);
322    }
323
324    /**
325     * test openFile()
326     *  - regular file
327     *  - TODO: variations on the content URI
328     */
329    public void testOpenFile() throws MessagingException, IOException {
330        Account account1 = ProviderTestUtils.setupAccount("open-file", false, mMockContext);
331        account1.save(mMockContext);
332        final long message1Id = 1;
333        long attachment1Id = 1;
334        long attachment2Id = 2;
335
336        // Note:  There is an implicit assumption in this test sequence that the first
337        // attachment we add will be id=1 and the 2nd will have id=2.  This could fail on
338        // a legitimate implementation.  Asserts below will catch this and fail the test
339        // if necessary.
340        Uri file1Uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment1Id);
341        Uri file2Uri = AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id);
342
343        // Test with no attachment found
344        AssetFileDescriptor afd;
345        try {
346            afd = mMockResolver.openAssetFileDescriptor(file1Uri, "r");
347            fail("Should throw an exception on a missing attachment entry");
348        } catch (FileNotFoundException fnf) {
349            // expected
350        }
351
352        // Add an attachment (but no associated file)
353        Attachment newAttachment = ProviderTestUtils.setupAttachment(message1Id, "file", 100,
354                false, mMockContext);
355        attachment1Id = addAttachmentToDb(account1, newAttachment);
356        assertEquals("Broken test:  Unexpected id assignment", 1, attachment1Id);
357
358        // Test with an attached database, attachment entry found, but no attachment found
359        try {
360            afd = mMockResolver.openAssetFileDescriptor(file1Uri, "r");
361            fail("Should throw an exception on a missing attachment file");
362        } catch (FileNotFoundException fnf) {
363            // expected
364        }
365
366        // Create an "attachment" by copying an image resource into a file
367        /* String fileName = */ createAttachmentFile(account1, attachment2Id);
368        Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1Id, "file", 100,
369                false, mMockContext);
370        newAttachment2.mContentId = null;
371        newAttachment2.setContentUri(
372                AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id)
373                .toString());
374        newAttachment2.mMimeType = "image/png";
375        attachment2Id = addAttachmentToDb(account1, newAttachment2);
376        assertEquals("Broken test:  Unexpected id assignment", 2, attachment2Id);
377
378        // Test with an attached database, attachment entry found - returns a file
379        afd = mMockResolver.openAssetFileDescriptor(file2Uri, "r");
380        assertNotNull(afd);
381        // TODO: Confirm it's the "right" file?
382        afd.close();
383    }
384
385    /**
386     * test openFile()
387     *  - thumbnail
388     * @throws IOException
389     *
390     * TODO:  The thumbnail mode returns null for its failure cases (and in one case, throws
391     * an SQLiteException).  The ContentResolver contract requires throwing FileNotFoundException
392     * in all of the non-success cases, and the provider should be fixed for consistency.
393     */
394    public void testOpenThumbnail() throws MessagingException, IOException {
395        Account account1 = ProviderTestUtils.setupAccount("open-thumbnail", false, mMockContext);
396        account1.save(mMockContext);
397        final long message1Id = 1;
398        long attachment1Id = 1;
399        long attachment2Id = 2;
400
401        // Note:  There is an implicit assumption in this test sequence that the first
402        // attachment we add will be id=1 and the 2nd will have id=2.  This could fail on
403        // a legitimate implementation.  Asserts below will catch this and fail the test
404        // if necessary.
405        Uri thumb1Uri = AttachmentUtilities.getAttachmentThumbnailUri(account1.mId,
406                attachment1Id, 62, 62);
407        Uri thumb2Uri = AttachmentUtilities.getAttachmentThumbnailUri(account1.mId,
408                attachment2Id, 62, 62);
409
410        // Test with an attached database, but no attachment found
411        AssetFileDescriptor afd = mMockResolver.openAssetFileDescriptor(thumb1Uri, "r");
412        assertNull(afd);
413
414        // Add an attachment (but no associated file)
415        Attachment newAttachment = ProviderTestUtils.setupAttachment(message1Id, "file", 100,
416                false, mMockContext);
417        attachment1Id = addAttachmentToDb(account1, newAttachment);
418        assertEquals("Broken test:  Unexpected id assignment", 1, attachment1Id);
419
420        // Test with an attached database, attachment entry found, but no attachment found
421        afd = mMockResolver.openAssetFileDescriptor(thumb1Uri, "r");
422        assertNull(afd);
423
424        // Create an "attachment" by copying an image resource into a file
425        /* String fileName = */ createAttachmentFile(account1, attachment2Id);
426        Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1Id, "file", 100,
427                false, mMockContext);
428        newAttachment2.mContentId = null;
429        newAttachment2.setContentUri(
430                AttachmentUtilities.getAttachmentUri(account1.mId, attachment2Id)
431                .toString());
432        newAttachment2.mMimeType = "image/png";
433        attachment2Id = addAttachmentToDb(account1, newAttachment2);
434        assertEquals("Broken test:  Unexpected id assignment", 2, attachment2Id);
435
436        // Test with an attached database, attachment entry found - returns a thumbnail
437        afd = mMockResolver.openAssetFileDescriptor(thumb2Uri, "r");
438        assertNotNull(afd);
439        // TODO: Confirm it's the "right" file?
440        afd.close();
441    }
442
443    private Uri createAttachment(Account account, long messageId, String contentUriStr) {
444        // Add an attachment entry.
445        Attachment newAttachment = ProviderTestUtils.setupAttachment(messageId, "file", 100,
446                false, mMockContext);
447        newAttachment.setContentUri(contentUriStr);
448        long attachmentId = addAttachmentToDb(account, newAttachment);
449        Uri attachmentUri = AttachmentUtilities.getAttachmentUri(account.mId, attachmentId);
450        return attachmentUri;
451    }
452
453    /**
454     * test resolveAttachmentIdToContentUri()
455     *  - without DB
456     *  - not in DB
457     *  - in DB, with not-null contentUri
458     *  - in DB, with null contentUri
459     */
460    public void testResolveAttachmentIdToContentUri() throws MessagingException {
461        Account account1 = ProviderTestUtils.setupAccount("attachment-query", false, mMockContext);
462        account1.save(mMockContext);
463        final long message1Id = 1;
464        // We use attachmentId == 1 but any other id would do
465        final long attachment1Id = 1;
466        final Uri attachment1Uri = AttachmentUtilities.getAttachmentUri(account1.mId,
467                attachment1Id);
468
469        // Test with no attachment found - should return input
470        // We know that the attachmentId 1 does not exist because there are no attachments
471        // created at this point
472        Uri result = AttachmentUtilities.resolveAttachmentIdToContentUri(
473                mMockResolver, attachment1Uri);
474        assertEquals(attachment1Uri, result);
475
476        // Test with existing attachement and contentUri != null
477        // Note, resolveAttachmentIdToContentUri() just uses
478        // the DB, and does not sample the files, so we won't bother creating the files
479        {
480            Uri attachmentUri = createAttachment(account1, message1Id, "file:///path/to/file");
481            Uri contentUri = AttachmentUtilities.resolveAttachmentIdToContentUri(
482                    mMockResolver, attachmentUri);
483            // When the attachment is found, return the stored content_uri value
484            assertEquals("file:///path/to/file", contentUri.toString());
485        }
486
487        // Test with existing attachement and contentUri == null
488        {
489            Uri attachmentUri = createAttachment(account1, message1Id, null);
490            Uri contentUri = AttachmentUtilities.resolveAttachmentIdToContentUri(
491                    mMockResolver, attachmentUri);
492            // When contentUri is null should return input
493            assertEquals(attachmentUri, contentUri);
494        }
495    }
496
497    /**
498     * Test the functionality of deleting all attachment files for a given message.
499     */
500    public void testDeleteFiles() throws IOException {
501        Account account1 = ProviderTestUtils.setupAccount("attachment-query", false, mMockContext);
502        account1.save(mMockContext);
503        final long message1Id = 1;      // 1 attachment, 1 file
504        final long message2Id = 2;      // 2 attachments, 2 files
505        final long message3Id = 3;      // 1 attachment, missing file
506        final long message4Id = 4;      // no attachments
507
508        // Add attachment entries for various test messages
509        Attachment newAttachment1 = ProviderTestUtils.setupAttachment(message1Id, "file1", 100,
510                true, mMockContext);
511        Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message2Id, "file2", 200,
512                true, mMockContext);
513        Attachment newAttachment3 = ProviderTestUtils.setupAttachment(message2Id, "file3", 100,
514                true, mMockContext);
515        Attachment newAttachment4 = ProviderTestUtils.setupAttachment(message3Id, "file4", 100,
516                true, mMockContext);
517
518        // Create test files
519        createAttachmentFile(account1, newAttachment1.mId);
520        createAttachmentFile(account1, newAttachment2.mId);
521        createAttachmentFile(account1, newAttachment3.mId);
522
523        // Confirm 3 attachment files found
524        File attachmentsDir = AttachmentUtilities.getAttachmentDirectory(mMockContext,
525                account1.mId);
526        assertEquals(3, attachmentsDir.listFiles().length);
527
528        // Command deletion of some files and check for results
529
530        // Message 4 has no attachments so no files should be deleted
531        AttachmentUtilities.deleteAllAttachmentFiles(mMockContext, account1.mId,
532                message4Id);
533        assertEquals(3, attachmentsDir.listFiles().length);
534
535        // Message 3 has no attachment files so no files should be deleted
536        AttachmentUtilities.deleteAllAttachmentFiles(mMockContext, account1.mId,
537                message3Id);
538        assertEquals(3, attachmentsDir.listFiles().length);
539
540        // Message 2 has 2 attachment files so this should delete 2 files
541        AttachmentUtilities.deleteAllAttachmentFiles(mMockContext, account1.mId,
542                message2Id);
543        assertEquals(1, attachmentsDir.listFiles().length);
544
545        // Message 1 has 1 attachment file so this should delete the last file
546        AttachmentUtilities.deleteAllAttachmentFiles(mMockContext, account1.mId,
547                message1Id);
548        assertEquals(0, attachmentsDir.listFiles().length);
549    }
550
551    /**
552     * Test the functionality of deleting an entire mailbox's attachments.
553     */
554    public void testDeleteMailbox() throws IOException {
555        Account account1 = ProviderTestUtils.setupAccount("attach-mbox-del", false, mMockContext);
556        account1.save(mMockContext);
557        long account1Id = account1.mId;
558        Mailbox mailbox1 = ProviderTestUtils.setupMailbox("mbox1", account1Id, true, mMockContext);
559        long mailbox1Id = mailbox1.mId;
560        Mailbox mailbox2 = ProviderTestUtils.setupMailbox("mbox2", account1Id, true, mMockContext);
561        long mailbox2Id = mailbox2.mId;
562
563        // Fill each mailbox with messages & attachments
564        populateAccountMailbox(account1, mailbox1Id, 3);
565        populateAccountMailbox(account1, mailbox2Id, 1);
566
567        // Confirm four attachment files found
568        File attachmentsDir = AttachmentUtilities.getAttachmentDirectory(mMockContext,
569                account1.mId);
570        assertEquals(4, attachmentsDir.listFiles().length);
571
572        // Command the deletion of mailbox 1 - we should lose 3 attachment files
573        AttachmentUtilities.deleteAllMailboxAttachmentFiles(mMockContext, account1Id,
574                mailbox1Id);
575        assertEquals(1, attachmentsDir.listFiles().length);
576
577        // Command the deletion of mailbox 2 - we should lose 1 attachment file
578        AttachmentUtilities.deleteAllMailboxAttachmentFiles(mMockContext, account1Id,
579                mailbox2Id);
580        assertEquals(0, attachmentsDir.listFiles().length);
581    }
582
583    /**
584     * Test the functionality of deleting an entire account's attachments.
585     */
586    public void testDeleteAccount() throws IOException {
587        Account account1 = ProviderTestUtils.setupAccount("attach-acct-del1", false, mMockContext);
588        account1.save(mMockContext);
589        long account1Id = account1.mId;
590        Mailbox mailbox1 = ProviderTestUtils.setupMailbox("mbox1", account1Id, true, mMockContext);
591        long mailbox1Id = mailbox1.mId;
592        Mailbox mailbox2 = ProviderTestUtils.setupMailbox("mbox2", account1Id, true, mMockContext);
593        long mailbox2Id = mailbox2.mId;
594
595        // Repeat for account #2
596        Account account2 = ProviderTestUtils.setupAccount("attach-acct-del2", false, mMockContext);
597        account2.save(mMockContext);
598        long account2Id = account2.mId;
599        Mailbox mailbox3 = ProviderTestUtils.setupMailbox("mbox3", account2Id, true, mMockContext);
600        long mailbox3Id = mailbox3.mId;
601        Mailbox mailbox4 = ProviderTestUtils.setupMailbox("mbox4", account2Id, true, mMockContext);
602        long mailbox4Id = mailbox4.mId;
603
604        // Fill each mailbox with messages & attachments
605        populateAccountMailbox(account1, mailbox1Id, 3);
606        populateAccountMailbox(account1, mailbox2Id, 1);
607        populateAccountMailbox(account2, mailbox3Id, 5);
608        populateAccountMailbox(account2, mailbox4Id, 2);
609
610        // Confirm eleven attachment files found
611        File directory1 = AttachmentUtilities.getAttachmentDirectory(mMockContext,
612                account1.mId);
613        assertEquals(4, directory1.listFiles().length);
614        File directory2 = AttachmentUtilities.getAttachmentDirectory(mMockContext,
615                account2.mId);
616        assertEquals(7, directory2.listFiles().length);
617
618        // Command the deletion of account 1 - we should lose 4 attachment files
619        AttachmentUtilities.deleteAllAccountAttachmentFiles(mMockContext, account1Id);
620        assertEquals(0, directory1.listFiles().length);
621        assertEquals(7, directory2.listFiles().length);
622
623        // Command the deletion of account 2 - we should lose 7 attachment file
624        AttachmentUtilities.deleteAllAccountAttachmentFiles(mMockContext, account2Id);
625        assertEquals(0, directory1.listFiles().length);
626        assertEquals(0, directory2.listFiles().length);
627    }
628
629    /**
630     * Create a set of attachments for a given test account and mailbox.  Creates the following:
631     *  Two messages per mailbox, one w/attachments, one w/o attachments
632     *  Any number of attachments (on the first message)
633     *  @param account the account to populate
634     *  @param mailboxId the mailbox to populate
635     *  @param numAttachments how many attachments to create
636     */
637    private void populateAccountMailbox(Account account, long mailboxId, int numAttachments)
638            throws IOException {
639        long accountId = account.mId;
640
641        // two messages per mailbox, one w/attachments, one w/o attachments
642        Message message1a = ProviderTestUtils.setupMessage(
643                "msg1a", accountId, mailboxId, false, true, mMockContext);
644        /* Message message1b = */ ProviderTestUtils.setupMessage(
645                "msg1b", accountId, mailboxId, false, true, mMockContext);
646
647        // Create attachment records & files
648        for (int count = 0; count < numAttachments; count++) {
649            Attachment newAttachment = ProviderTestUtils.setupAttachment(message1a.mId,
650                    "file" + count, 100 * count, true, mMockContext);
651            createAttachmentFile(account, newAttachment.mId);
652        }
653    }
654
655    /**
656     * Create an attachment by copying an image resource into a file.  Uses "real" resources
657     * to get a real image from Email
658     */
659    private String createAttachmentFile(Account forAccount, long id) throws IOException {
660        File outFile = getAttachmentFile(forAccount, id);
661        Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(),
662                R.drawable.ic_attach_file_18dp);
663        FileOutputStream out = new FileOutputStream(outFile);
664        bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
665        out.close();
666
667        return outFile.getAbsolutePath();
668    }
669
670    /**
671     * Record an attachment in the attachments database
672     * @return the id of the attachment just created
673     */
674    private long addAttachmentToDb(Account forAccount, Attachment newAttachment) {
675        newAttachment.save(mMockContext);
676        return newAttachment.mId;
677    }
678
679    /**
680     * Map from account, attachment ID to attachment file
681     */
682    private File getAttachmentFile(Account forAccount, long id) {
683        String idString = Long.toString(id);
684        File attachmentsDir = mMockContext.getDatabasePath(forAccount.mId + ".db_att");
685        if (!attachmentsDir.exists()) {
686            attachmentsDir.mkdirs();
687        }
688        return new File(attachmentsDir, idString);
689    }
690}
691