• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AttachmentProviderTests()65     public AttachmentProviderTests() {
66         super(AttachmentProvider.class, AttachmentProvider.AUTHORITY);
67     }
68 
69     @Override
setUp()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
tearDown()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      */
testUnimplemented()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      */
testQuery()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      */
testGetType()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      */
testInferMimeType()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      */
testOpenFile()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      */
testOpenThumbnail()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 
createAttachment(Account account, long messageId, String contentUriStr)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      */
testResolveAttachmentIdToContentUri()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      */
testDeleteFiles()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      */
testDeleteMailbox()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      */
createAttachmentFile(Account forAccount, long id)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      */
setupAttachmentDatabase(Account forAccount)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      */
addAttachmentToDb(Account forAccount, Attachment newAttachment)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      */
dbName(Account forAccount)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      */
getAttachmentFile(Account forAccount, long id)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