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