Home | History | Annotate | Download | only in provider
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.email.provider;
     18 
     19 import com.android.email.R;
     20 import com.android.email.mail.MessagingException;
     21 import com.android.email.mail.store.LocalStore;
     22 import com.android.email.provider.AttachmentProvider.AttachmentProviderColumns;
     23 import com.android.email.provider.EmailContent.Account;
     24 import com.android.email.provider.EmailContent.Attachment;
     25 import com.android.email.provider.EmailContent.Mailbox;
     26 import com.android.email.provider.EmailContent.Message;
     27 
     28 import android.content.ContentResolver;
     29 import android.content.ContentValues;
     30 import android.content.Context;
     31 import android.content.res.AssetFileDescriptor;
     32 import android.database.Cursor;
     33 import android.database.sqlite.SQLiteDatabase;
     34 import android.graphics.Bitmap;
     35 import android.graphics.BitmapFactory;
     36 import android.net.Uri;
     37 import android.test.ProviderTestCase2;
     38 import android.test.mock.MockContentResolver;
     39 
     40 import java.io.File;
     41 import java.io.FileNotFoundException;
     42 import java.io.FileOutputStream;
     43 import 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 public class AttachmentProviderTests extends ProviderTestCase2<AttachmentProvider> {
     52 
     53     /*
     54      * This switch will enable us to transition these tests, and the AttachmentProvider, from the
     55      * "old" LocalStore model to the "new" provider model.  After the transition is complete,
     56      * this flag (and its associated code) can be removed.
     57      */
     58     private final boolean USE_LOCALSTORE = false;
     59     LocalStore mLocalStore = null;
     60 
     61     EmailProvider mEmailProvider;
     62     Context mMockContext;
     63     ContentResolver mMockResolver;
     64 
     65     public AttachmentProviderTests() {
     66         super(AttachmentProvider.class, AttachmentProvider.AUTHORITY);
     67     }
     68 
     69     @Override
     70     public void setUp() throws Exception {
     71         super.setUp();
     72         mMockContext = getMockContext();
     73         mMockResolver = mMockContext.getContentResolver();
     74 
     75         // Spin up an Email provider as well and put it under the same mock test framework
     76         mEmailProvider = new EmailProvider();
     77         mEmailProvider.attachInfo(mMockContext, null);
     78         assertNotNull(mEmailProvider);
     79         ((MockContentResolver) mMockResolver)
     80                 .addProvider(EmailProvider.EMAIL_AUTHORITY, mEmailProvider);
     81     }
     82 
     83     @Override
     84     public void tearDown() throws Exception {
     85         super.tearDown();
     86 
     87         if (mLocalStore != null) {
     88             mLocalStore.delete();
     89         }
     90     }
     91 
     92     /**
     93      * test delete() - should do nothing
     94      * test update() - should do nothing
     95      * test insert() - should do nothing
     96      */
     97     public void testUnimplemented() {
     98         assertEquals(0, mMockResolver.delete(AttachmentProvider.CONTENT_URI, null, null));
     99         assertEquals(0, mMockResolver.update(AttachmentProvider.CONTENT_URI, null, null, null));
    100         assertEquals(null, mMockResolver.insert(AttachmentProvider.CONTENT_URI, null));
    101     }
    102 
    103     /**
    104      * test query()
    105      *  - item found
    106      *  - item not found
    107      *  - permuted projection
    108      */
    109     public void testQuery() throws MessagingException {
    110         Account account1 = ProviderTestUtils.setupAccount("attachment-query", false, mMockContext);
    111         account1.mCompatibilityUuid = "test-UUID";
    112         account1.save(mMockContext);
    113         final long message1Id = 1;
    114         long attachment1Id = 1;
    115         long attachment2Id = 2;
    116         long attachment3Id = 3;
    117 
    118         // Note:  There is an implicit assumption in this test sequence that the first
    119         // attachment we add will be id=1 and the 2nd will have id=2.  This could fail on
    120         // a legitimate implementation.  Asserts below will catch this and fail the test
    121         // if necessary.
    122         Uri attachment1Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
    123         Uri attachment2Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id);
    124         Uri attachment3Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment3Id);
    125 
    126         // Test with no attached database - should return null
    127         Cursor c = mMockResolver.query(attachment1Uri, (String[])null, null, (String[])null, null);
    128         assertNull(c);
    129 
    130         // Test with an attached database, but no attachment found - should return null
    131         setupAttachmentDatabase(account1);
    132         c = mMockResolver.query(attachment1Uri, (String[])null, null, (String[])null, null);
    133         assertNull(c);
    134 
    135         // Add a couple of attachment entries.  Note, query() just uses the DB, and does not
    136         // sample the files, so we won't bother creating the files
    137         Attachment newAttachment1 = ProviderTestUtils.setupAttachment(message1Id, "file1", 100,
    138                 false, mMockContext);
    139         newAttachment1.mContentUri =
    140             AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id).toString();
    141         attachment1Id = addAttachmentToDb(account1, newAttachment1);
    142         assertEquals("Broken test:  Unexpected id assignment", 1, attachment1Id);
    143 
    144         Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1Id, "file2", 200,
    145                 false, mMockContext);
    146         newAttachment2.mContentUri =
    147             AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id).toString();
    148         attachment2Id = addAttachmentToDb(account1, newAttachment2);
    149         assertEquals("Broken test:  Unexpected id assignment", 2, attachment2Id);
    150 
    151         Attachment newAttachment3 = ProviderTestUtils.setupAttachment(message1Id, "file3", 300,
    152                 false, mMockContext);
    153         newAttachment3.mContentUri =
    154             AttachmentProvider.getAttachmentUri(account1.mId, attachment3Id).toString();
    155         attachment3Id = addAttachmentToDb(account1, newAttachment3);
    156         assertEquals("Broken test:  Unexpected id assignment", 3, attachment3Id);
    157 
    158         // Return a row with all columns specified
    159         attachment2Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id);
    160         c = mMockResolver.query(
    161                 attachment2Uri,
    162                 new String[] { AttachmentProviderColumns._ID, AttachmentProviderColumns.DATA,
    163                                AttachmentProviderColumns.DISPLAY_NAME,
    164                                AttachmentProviderColumns.SIZE },
    165                 null, null, null);
    166         assertEquals(1, c.getCount());
    167         assertTrue(c.moveToFirst());
    168         assertEquals(attachment2Id, c.getLong(0));                  // id
    169         assertEquals(attachment2Uri.toString(), c.getString(1));    // content URI
    170         assertEquals("file2", c.getString(2));                      // display name
    171         assertEquals(200, c.getInt(3));                             // size
    172 
    173         // Return a row with permuted columns
    174         attachment3Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment3Id);
    175         c = mMockResolver.query(
    176                 attachment3Uri,
    177                 new String[] { AttachmentProviderColumns.SIZE,
    178                                AttachmentProviderColumns.DISPLAY_NAME,
    179                                AttachmentProviderColumns.DATA, AttachmentProviderColumns._ID },
    180                 null, null, null);
    181         assertEquals(1, c.getCount());
    182         assertTrue(c.moveToFirst());
    183         assertEquals(attachment3Id, c.getLong(3));                  // id
    184         assertEquals(attachment3Uri.toString(), c.getString(2));    // content URI
    185         assertEquals("file3", c.getString(1));                      // display name
    186         assertEquals(300, c.getInt(0));                             // size
    187     }
    188 
    189     /**
    190      * test getType()
    191      *  - regular file
    192      *  - thumbnail
    193      */
    194     public void testGetType() throws MessagingException {
    195         Account account1 = ProviderTestUtils.setupAccount("get-type", false, mMockContext);
    196         account1.mCompatibilityUuid = "test-UUID";
    197         account1.save(mMockContext);
    198         final long message1Id = 1;
    199         long attachment1Id = 1;
    200         long attachment2Id = 2;
    201         long attachment3Id = 3;
    202         long attachment4Id = 4;
    203         long attachment5Id = 5;
    204         long attachment6Id = 6;
    205 
    206         Uri attachment1Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
    207 
    208         // Test with no attached database - should return null
    209         String type = mMockResolver.getType(attachment1Uri);
    210         assertNull(type);
    211 
    212         // Test with an attached database, but no attachment found - should return null
    213         setupAttachmentDatabase(account1);
    214         type = mMockResolver.getType(attachment1Uri);
    215         assertNull(type);
    216 
    217         // Add a couple of attachment entries.  Note, getType() just uses the DB, and does not
    218         // sample the files, so we won't bother creating the files
    219         Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1Id, "file2", 100,
    220                 false, mMockContext);
    221         newAttachment2.mMimeType = "image/jpg";
    222         attachment2Id = addAttachmentToDb(account1, newAttachment2);
    223 
    224         Attachment newAttachment3 = ProviderTestUtils.setupAttachment(message1Id, "file3", 100,
    225                 false, mMockContext);
    226         newAttachment3.mMimeType = "text/plain";
    227         attachment3Id = addAttachmentToDb(account1, newAttachment3);
    228 
    229         Attachment newAttachment4 = ProviderTestUtils.setupAttachment(message1Id, "file4.doc", 100,
    230                 false, mMockContext);
    231         newAttachment4.mMimeType = "application/octet-stream";
    232         attachment4Id = addAttachmentToDb(account1, newAttachment4);
    233 
    234         Attachment newAttachment5 = ProviderTestUtils.setupAttachment(message1Id, "file5.xyz", 100,
    235                 false, mMockContext);
    236         newAttachment5.mMimeType = "application/octet-stream";
    237         attachment5Id = addAttachmentToDb(account1, newAttachment5);
    238 
    239         Attachment newAttachment6 = ProviderTestUtils.setupAttachment(message1Id, "file6", 100,
    240                 false, mMockContext);
    241         newAttachment6.mMimeType = "";
    242         attachment6Id = addAttachmentToDb(account1, newAttachment6);
    243 
    244         // Check the returned filetypes
    245         Uri uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id);
    246         type = mMockResolver.getType(uri);
    247         assertEquals("image/jpg", type);
    248         uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment3Id);
    249         type = mMockResolver.getType(uri);
    250         assertEquals("text/plain", type);
    251         uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment4Id);
    252         type = mMockResolver.getType(uri);
    253         assertEquals("application/msword", type);
    254         uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment5Id);
    255         type = mMockResolver.getType(uri);
    256         assertEquals("application/xyz", type);
    257         uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment6Id);
    258         type = mMockResolver.getType(uri);
    259         assertEquals("application/octet-stream", type);
    260 
    261         // Check the returned filetypes for the thumbnails
    262         uri = AttachmentProvider.getAttachmentThumbnailUri(account1.mId, attachment2Id, 62, 62);
    263         type = mMockResolver.getType(uri);
    264         assertEquals("image/png", type);
    265         uri = AttachmentProvider.getAttachmentThumbnailUri(account1.mId, attachment3Id, 62, 62);
    266         type = mMockResolver.getType(uri);
    267         assertEquals("image/png", type);
    268     }
    269 
    270     /**
    271      * Test static inferMimeType()
    272      * From the method doc:
    273      *   If the given mime type is non-empty and anything other than "application/octet-stream",
    274      *   just return it.  (This is the most common case.)
    275      *   If the filename has a recognizable extension and it converts to a mime type, return that.
    276      *   If the filename has an unrecognized extension, return "application/extension"
    277      *   Otherwise return "application/octet-stream".
    278      */
    279     public void testInferMimeType() {
    280         final String DEFAULT = "application/octet-stream";
    281         final String FILE_PDF = "myfile.false.pdf";
    282         final String FILE_ABC = "myfile.false.abc";
    283         final String FILE_NO_EXT = "myfile";
    284 
    285         // If the given mime type is non-empty and anything other than "application/octet-stream",
    286         // just return it.  (This is the most common case.)
    287         assertEquals("mime/type", AttachmentProvider.inferMimeType(null, "mime/type"));
    288         assertEquals("mime/type", AttachmentProvider.inferMimeType("", "mime/type"));
    289         assertEquals("mime/type", AttachmentProvider.inferMimeType(FILE_PDF, "mime/type"));
    290 
    291         // If the filename has a recognizable extension and it converts to a mime type, return that.
    292         assertEquals("application/pdf", AttachmentProvider.inferMimeType(FILE_PDF, null));
    293         assertEquals("application/pdf", AttachmentProvider.inferMimeType(FILE_PDF, ""));
    294         assertEquals("application/pdf", AttachmentProvider.inferMimeType(FILE_PDF, DEFAULT));
    295 
    296         // If the filename has an unrecognized extension, return "application/extension"
    297         assertEquals("application/abc", AttachmentProvider.inferMimeType(FILE_ABC, null));
    298         assertEquals("application/abc", AttachmentProvider.inferMimeType(FILE_ABC, ""));
    299         assertEquals("application/abc", AttachmentProvider.inferMimeType(FILE_ABC, DEFAULT));
    300 
    301         // Otherwise return "application/octet-stream".
    302         assertEquals(DEFAULT, AttachmentProvider.inferMimeType(FILE_NO_EXT, null));
    303         assertEquals(DEFAULT, AttachmentProvider.inferMimeType(FILE_NO_EXT, ""));
    304         assertEquals(DEFAULT, AttachmentProvider.inferMimeType(FILE_NO_EXT, DEFAULT));
    305         assertEquals(DEFAULT, AttachmentProvider.inferMimeType(null, null));
    306         assertEquals(DEFAULT, AttachmentProvider.inferMimeType("", ""));
    307     }
    308 
    309     /**
    310      * test openFile()
    311      *  - regular file
    312      *  - TODO: variations on the content URI
    313      */
    314     public void testOpenFile() throws MessagingException, IOException {
    315         Account account1 = ProviderTestUtils.setupAccount("open-file", false, mMockContext);
    316         account1.mCompatibilityUuid = "test-UUID";
    317         account1.save(mMockContext);
    318         final long message1Id = 1;
    319         long attachment1Id = 1;
    320         long attachment2Id = 2;
    321 
    322         // Note:  There is an implicit assumption in this test sequence that the first
    323         // attachment we add will be id=1 and the 2nd will have id=2.  This could fail on
    324         // a legitimate implementation.  Asserts below will catch this and fail the test
    325         // if necessary.
    326         Uri file1Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
    327         Uri file2Uri = AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id);
    328 
    329         // Test with no attached database - should throw an exception
    330         AssetFileDescriptor afd;
    331         try {
    332             afd = mMockResolver.openAssetFileDescriptor(file1Uri, "r");
    333             fail("Should throw an exception on a bad URI");
    334         } catch (FileNotFoundException fnf) {
    335             // expected
    336         }
    337 
    338         // Test with an attached database, but no attachment found
    339         setupAttachmentDatabase(account1);
    340         try {
    341             afd = mMockResolver.openAssetFileDescriptor(file1Uri, "r");
    342             fail("Should throw an exception on a missing attachment entry");
    343         } catch (FileNotFoundException fnf) {
    344             // expected
    345         }
    346 
    347         // Add an attachment (but no associated file)
    348         Attachment newAttachment = ProviderTestUtils.setupAttachment(message1Id, "file", 100,
    349                 false, mMockContext);
    350         attachment1Id = addAttachmentToDb(account1, newAttachment);
    351         assertEquals("Broken test:  Unexpected id assignment", 1, attachment1Id);
    352 
    353         // Test with an attached database, attachment entry found, but no attachment found
    354         try {
    355             afd = mMockResolver.openAssetFileDescriptor(file1Uri, "r");
    356             fail("Should throw an exception on a missing attachment file");
    357         } catch (FileNotFoundException fnf) {
    358             // expected
    359         }
    360 
    361         // Create an "attachment" by copying an image resource into a file
    362         /* String fileName = */ createAttachmentFile(account1, attachment2Id);
    363         Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1Id, "file", 100,
    364                 false, mMockContext);
    365         newAttachment2.mContentId = null;
    366         newAttachment2.mContentUri =
    367                 AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id).toString();
    368         newAttachment2.mMimeType = "image/png";
    369         attachment2Id = addAttachmentToDb(account1, newAttachment2);
    370         assertEquals("Broken test:  Unexpected id assignment", 2, attachment2Id);
    371 
    372         // Test with an attached database, attachment entry found - returns a file
    373         afd = mMockResolver.openAssetFileDescriptor(file2Uri, "r");
    374         assertNotNull(afd);
    375         // TODO: Confirm it's the "right" file?
    376         afd.close();
    377     }
    378 
    379     /**
    380      * test openFile()
    381      *  - thumbnail
    382      * @throws IOException
    383      *
    384      * TODO:  The thumbnail mode returns null for its failure cases (and in one case, throws
    385      * an SQLiteException).  The ContentResolver contract requires throwing FileNotFoundException
    386      * in all of the non-success cases, and the provider should be fixed for consistency.
    387      */
    388     public void testOpenThumbnail() throws MessagingException, IOException {
    389         Account account1 = ProviderTestUtils.setupAccount("open-thumbnail", false, mMockContext);
    390         account1.mCompatibilityUuid = "test-UUID";
    391         account1.save(mMockContext);
    392         final long message1Id = 1;
    393         long attachment1Id = 1;
    394         long attachment2Id = 2;
    395 
    396         // Note:  There is an implicit assumption in this test sequence that the first
    397         // attachment we add will be id=1 and the 2nd will have id=2.  This could fail on
    398         // a legitimate implementation.  Asserts below will catch this and fail the test
    399         // if necessary.
    400         Uri thumb1Uri = AttachmentProvider.getAttachmentThumbnailUri(account1.mId, attachment1Id,
    401                 62, 62);
    402         Uri thumb2Uri = AttachmentProvider.getAttachmentThumbnailUri(account1.mId, attachment2Id,
    403                 62, 62);
    404 
    405         // Test with no attached database - should return null (used to throw SQLiteException)
    406         AssetFileDescriptor afd = mMockResolver.openAssetFileDescriptor(thumb1Uri, "r");
    407         assertNull(afd);
    408 
    409         // Test with an attached database, but no attachment found
    410         setupAttachmentDatabase(account1);
    411         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.mContentUri =
    430                 AttachmentProvider.getAttachmentUri(account1.mId, attachment2Id).toString();
    431         newAttachment2.mMimeType = "image/png";
    432         attachment2Id = addAttachmentToDb(account1, newAttachment2);
    433         assertEquals("Broken test:  Unexpected id assignment", 2, attachment2Id);
    434 
    435         // Test with an attached database, attachment entry found - returns a thumbnail
    436         afd = mMockResolver.openAssetFileDescriptor(thumb2Uri, "r");
    437         assertNotNull(afd);
    438         // TODO: Confirm it's the "right" file?
    439         afd.close();
    440     }
    441 
    442     private Uri createAttachment(Account account, long messageId, String contentUriStr) {
    443         // Add an attachment entry.
    444         Attachment newAttachment = ProviderTestUtils.setupAttachment(messageId, "file", 100,
    445                 false, mMockContext);
    446         newAttachment.mContentUri = contentUriStr;
    447         long attachmentId = addAttachmentToDb(account, newAttachment);
    448         Uri attachmentUri = AttachmentProvider.getAttachmentUri(account.mId, attachmentId);
    449         return attachmentUri;
    450     }
    451 
    452     /**
    453      * test resolveAttachmentIdToContentUri()
    454      *  - without DB
    455      *  - not in DB
    456      *  - in DB, with not-null contentUri
    457      *  - in DB, with null contentUri
    458      */
    459     public void testResolveAttachmentIdToContentUri() throws MessagingException {
    460         Account account1 = ProviderTestUtils.setupAccount("attachment-query", false, mMockContext);
    461         account1.mCompatibilityUuid = "test-UUID";
    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 = AttachmentProvider.getAttachmentUri(account1.mId, attachment1Id);
    467 
    468         // Test with no attached database - should return input
    469         Uri result = AttachmentProvider.resolveAttachmentIdToContentUri(
    470                 mMockResolver, attachment1Uri);
    471         assertEquals(attachment1Uri, result);
    472 
    473         setupAttachmentDatabase(account1);
    474 
    475         // Test with an attached database, but no attachment found - should return input
    476         // We know that the attachmentId 1 does not exist because there are no attachments
    477         // created at this point
    478         result = AttachmentProvider.resolveAttachmentIdToContentUri(
    479                 mMockResolver, attachment1Uri);
    480         assertEquals(attachment1Uri, result);
    481 
    482         // Test with existing attachement and contentUri != null
    483         // Note, resolveAttachmentIdToContentUri() just uses
    484         // the DB, and does not sample the files, so we won't bother creating the files
    485         {
    486             Uri attachmentUri = createAttachment(account1, message1Id, "file:///path/to/file");
    487             Uri contentUri = AttachmentProvider.resolveAttachmentIdToContentUri(mMockResolver,
    488                     attachmentUri);
    489             // When the attachment is found, return the stored content_uri value
    490             assertEquals("file:///path/to/file", contentUri.toString());
    491         }
    492 
    493         // Test with existing attachement and contentUri == null
    494         {
    495             Uri attachmentUri = createAttachment(account1, message1Id, null);
    496             Uri contentUri = AttachmentProvider.resolveAttachmentIdToContentUri(mMockResolver,
    497                     attachmentUri);
    498             // When contentUri is null should return input
    499             assertEquals(attachmentUri, contentUri);
    500         }
    501     }
    502 
    503     /**
    504      * Test the functionality of deleting all attachment files for a given message.
    505      */
    506     public void testDeleteFiles() throws IOException {
    507         Account account1 = ProviderTestUtils.setupAccount("attachment-query", false, mMockContext);
    508         account1.mCompatibilityUuid = "test-UUID";
    509         account1.save(mMockContext);
    510         final long message1Id = 1;      // 1 attachment, 1 file
    511         final long message2Id = 2;      // 2 attachments, 2 files
    512         final long message3Id = 3;      // 1 attachment, missing file
    513         final long message4Id = 4;      // no attachments
    514 
    515         // Add attachment entries for various test messages
    516         Attachment newAttachment1 = ProviderTestUtils.setupAttachment(message1Id, "file1", 100,
    517                 true, mMockContext);
    518         Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message2Id, "file2", 200,
    519                 true, mMockContext);
    520         Attachment newAttachment3 = ProviderTestUtils.setupAttachment(message2Id, "file3", 100,
    521                 true, mMockContext);
    522         Attachment newAttachment4 = ProviderTestUtils.setupAttachment(message3Id, "file4", 100,
    523                 true, mMockContext);
    524 
    525         // Create test files
    526         createAttachmentFile(account1, newAttachment1.mId);
    527         createAttachmentFile(account1, newAttachment2.mId);
    528         createAttachmentFile(account1, newAttachment3.mId);
    529 
    530         // Confirm 3 attachment files found
    531         File attachmentsDir = AttachmentProvider.getAttachmentDirectory(mMockContext, account1.mId);
    532         assertEquals(3, attachmentsDir.listFiles().length);
    533 
    534         // Command deletion of some files and check for results
    535 
    536         // Message 4 has no attachments so no files should be deleted
    537         AttachmentProvider.deleteAllAttachmentFiles(mMockContext, account1.mId, message4Id);
    538         assertEquals(3, attachmentsDir.listFiles().length);
    539 
    540         // Message 3 has no attachment files so no files should be deleted
    541         AttachmentProvider.deleteAllAttachmentFiles(mMockContext, account1.mId, message3Id);
    542         assertEquals(3, attachmentsDir.listFiles().length);
    543 
    544         // Message 2 has 2 attachment files so this should delete 2 files
    545         AttachmentProvider.deleteAllAttachmentFiles(mMockContext, account1.mId, message2Id);
    546         assertEquals(1, attachmentsDir.listFiles().length);
    547 
    548         // Message 1 has 1 attachment file so this should delete the last file
    549         AttachmentProvider.deleteAllAttachmentFiles(mMockContext, account1.mId, message1Id);
    550         assertEquals(0, attachmentsDir.listFiles().length);
    551     }
    552 
    553     /**
    554      * Test the functionality of deleting an entire mailbox's attachments.
    555      */
    556     public void testDeleteMailbox() throws IOException {
    557         Account account1 = ProviderTestUtils.setupAccount("attach-mbox-del", false, mMockContext);
    558         account1.mCompatibilityUuid = "test-UUID";
    559         account1.save(mMockContext);
    560         long account1Id = account1.mId;
    561         Mailbox mailbox1 = ProviderTestUtils.setupMailbox("mbox1", account1Id, true, mMockContext);
    562         long mailbox1Id = mailbox1.mId;
    563         Mailbox mailbox2 = ProviderTestUtils.setupMailbox("mbox2", account1Id, true, mMockContext);
    564         long mailbox2Id = mailbox2.mId;
    565 
    566         // two messages per mailbox, one w/attachments, one w/o attachments
    567         Message message1a = ProviderTestUtils.setupMessage("msg1a", account1Id, mailbox1Id, false,
    568                 true, mMockContext);
    569         Message message1b = ProviderTestUtils.setupMessage("msg1b", account1Id, mailbox1Id, false,
    570                 true, mMockContext);
    571         Message message2a = ProviderTestUtils.setupMessage("msg2a", account1Id, mailbox2Id, false,
    572                 true, mMockContext);
    573         Message message2b = ProviderTestUtils.setupMessage("msg2b", account1Id, mailbox2Id, false,
    574                 true, mMockContext);
    575 
    576         // attachments on each of the "a" messages (3 on 1a, 1 on 1b)
    577         Attachment newAttachment1 = ProviderTestUtils.setupAttachment(message1a.mId, "file1", 100,
    578                 true, mMockContext);
    579         Attachment newAttachment2 = ProviderTestUtils.setupAttachment(message1a.mId, "file2", 200,
    580                 true, mMockContext);
    581         Attachment newAttachment3 = ProviderTestUtils.setupAttachment(message1a.mId, "file3", 100,
    582                 true, mMockContext);
    583         Attachment newAttachment4 = ProviderTestUtils.setupAttachment(message2a.mId, "file4", 100,
    584                 true, mMockContext);
    585 
    586         // Create test files
    587         createAttachmentFile(account1, newAttachment1.mId);
    588         createAttachmentFile(account1, newAttachment2.mId);
    589         createAttachmentFile(account1, newAttachment3.mId);
    590         createAttachmentFile(account1, newAttachment4.mId);
    591 
    592         // Confirm four attachment files found
    593         File attachmentsDir = AttachmentProvider.getAttachmentDirectory(mMockContext, account1.mId);
    594         assertEquals(4, attachmentsDir.listFiles().length);
    595 
    596         // Command the deletion of mailbox 1 - we should lose 3 attachment files
    597         AttachmentProvider.deleteAllMailboxAttachmentFiles(mMockContext, account1Id, mailbox1Id);
    598         assertEquals(1, attachmentsDir.listFiles().length);
    599 
    600         // Command the deletion of mailbox 2 - we should lose 1 attachment file
    601         AttachmentProvider.deleteAllMailboxAttachmentFiles(mMockContext, account1Id, mailbox2Id);
    602         assertEquals(0, attachmentsDir.listFiles().length);
    603     }
    604 
    605 
    606     /**
    607      * Create an attachment by copying an image resource into a file.  Uses "real" resources
    608      * to get a real image from Email
    609      */
    610     private String createAttachmentFile(Account forAccount, long id) throws IOException {
    611         File outFile = getAttachmentFile(forAccount, id);
    612         Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(),
    613                 R.drawable.ic_email_attachment);
    614         FileOutputStream out = new FileOutputStream(outFile);
    615         bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
    616         out.close();
    617 
    618         return outFile.getAbsolutePath();
    619     }
    620 
    621     /**
    622      * Set up the attachments database.
    623      */
    624     private void setupAttachmentDatabase(Account forAccount) throws MessagingException {
    625         if (USE_LOCALSTORE) {
    626             String localStoreUri = "local://localhost/" + dbName(forAccount);
    627             mLocalStore = (LocalStore) LocalStore.newInstance(localStoreUri, mMockContext, null);
    628         } else {
    629             // Nothing to do - EmailProvider is already available for us
    630         }
    631     }
    632 
    633     /**
    634      * Record an attachment in the attachments database
    635      * @return the id of the attachment just created
    636      */
    637     private long addAttachmentToDb(Account forAccount, Attachment newAttachment) {
    638         long attachmentId = -1;
    639         if (USE_LOCALSTORE) {
    640             ContentValues cv = new ContentValues();
    641             cv.put("message_id", newAttachment.mMessageKey);
    642             cv.put("content_uri", newAttachment.mContentUri);
    643             cv.put("store_data", (String)null);
    644             cv.put("size", newAttachment.mSize);
    645             cv.put("name", newAttachment.mFileName);
    646             cv.put("mime_type", newAttachment.mMimeType);
    647             cv.put("content_id", newAttachment.mContentId);
    648 
    649             SQLiteDatabase db = null;
    650             try {
    651                 db = SQLiteDatabase.openDatabase(dbName(forAccount), null, 0);
    652                 attachmentId = db.insertOrThrow("attachments", "message_id", cv);
    653             }
    654             finally {
    655                 if (db != null) {
    656                     db.close();
    657                 }
    658             }
    659         } else {
    660             newAttachment.save(mMockContext);
    661             attachmentId = newAttachment.mId;
    662         }
    663         return attachmentId;
    664     }
    665 
    666     /**
    667      * Return the database path+name for a given account
    668      */
    669     private String dbName(Account forAccount) {
    670         if (USE_LOCALSTORE) {
    671             return mMockContext.getDatabasePath(forAccount.mCompatibilityUuid + ".db").toString();
    672         } else {
    673             throw new java.lang.UnsupportedOperationException();
    674         }
    675     }
    676 
    677     /**
    678      * Map from account, attachment ID to attachment file
    679      */
    680     private File getAttachmentFile(Account forAccount, long id) {
    681         String idString = Long.toString(id);
    682         if (USE_LOCALSTORE) {
    683             return new File(mMockContext.getDatabasePath(forAccount.mCompatibilityUuid + ".db_att"),
    684                     idString);
    685         } else {
    686             File attachmentsDir = mMockContext.getDatabasePath(forAccount.mId + ".db_att");
    687             if (!attachmentsDir.exists()) {
    688                 attachmentsDir.mkdirs();
    689             }
    690             return new File(attachmentsDir, idString);
    691         }
    692     }
    693 }
    694