• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.exchange.adapter;
18 
19 import android.content.ContentUris;
20 import android.content.ContentValues;
21 import android.test.suitebuilder.annotation.SmallTest;
22 
23 import com.android.emailcommon.provider.Account;
24 import com.android.emailcommon.provider.EmailContent;
25 import com.android.emailcommon.provider.EmailContent.Body;
26 import com.android.emailcommon.provider.EmailContent.Message;
27 import com.android.emailcommon.provider.EmailContent.MessageColumns;
28 import com.android.emailcommon.provider.EmailContent.SyncColumns;
29 import com.android.emailcommon.provider.Mailbox;
30 import com.android.exchange.EasSyncService;
31 import com.android.exchange.adapter.EmailSyncAdapter.EasEmailSyncParser;
32 import com.android.exchange.adapter.EmailSyncAdapter.EasEmailSyncParser.ServerChange;
33 import com.android.exchange.provider.EmailContentSetupUtils;
34 
35 import java.io.ByteArrayInputStream;
36 import java.io.IOException;
37 import java.util.ArrayList;
38 import java.util.GregorianCalendar;
39 import java.util.TimeZone;
40 @SmallTest
41 public class EmailSyncAdapterTests extends SyncAdapterTestCase<EmailSyncAdapter> {
42 
43     private static final String WHERE_ACCOUNT_KEY = Message.ACCOUNT_KEY + "=?";
44     private static final String[] ACCOUNT_ARGUMENT = new String[1];
45 
46     // A server id that is guaranteed to be test-related
47     private static final String TEST_SERVER_ID = "__1:22";
48 
EmailSyncAdapterTests()49     public EmailSyncAdapterTests() {
50         super();
51     }
52 
53     /**
54      * Check functionality for getting mime type from a file name (using its extension)
55      * The default for all unknown files is application/octet-stream
56      */
testGetMimeTypeFromFileName()57     public void testGetMimeTypeFromFileName() throws IOException {
58         EasSyncService service = getTestService();
59         EmailSyncAdapter adapter = new EmailSyncAdapter(service);
60         EasEmailSyncParser p = adapter.new EasEmailSyncParser(getTestInputStream(), adapter);
61         // Test a few known types
62         String mimeType = p.getMimeTypeFromFileName("foo.jpg");
63         assertEquals("image/jpeg", mimeType);
64         // Make sure this is case insensitive
65         mimeType = p.getMimeTypeFromFileName("foo.JPG");
66         assertEquals("image/jpeg", mimeType);
67         mimeType = p.getMimeTypeFromFileName("this_is_a_weird_filename.gif");
68         assertEquals("image/gif", mimeType);
69         // Test an illegal file name ending with the extension prefix
70         mimeType = p.getMimeTypeFromFileName("foo.");
71         assertEquals("application/octet-stream", mimeType);
72         // Test a really awful name
73         mimeType = p.getMimeTypeFromFileName(".....");
74         assertEquals("application/octet-stream", mimeType);
75         // Test a bare file name (no extension)
76         mimeType = p.getMimeTypeFromFileName("foo");
77         assertEquals("application/octet-stream", mimeType);
78         // And no name at all (null isn't a valid input)
79         mimeType = p.getMimeTypeFromFileName("");
80         assertEquals("application/octet-stream", mimeType);
81     }
82 
testFormatDateTime()83     public void testFormatDateTime() throws IOException {
84         EmailSyncAdapter adapter = getTestSyncAdapter(EmailSyncAdapter.class);
85         GregorianCalendar calendar = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
86         // Calendar is odd, months are zero based, so the first 11 below is December...
87         calendar.set(2008, 11, 11, 18, 19, 20);
88         String date = adapter.formatDateTime(calendar);
89         assertEquals("2008-12-11T18:19:20.000Z", date);
90         calendar.clear();
91         calendar.set(2012, 0, 2, 23, 0, 1);
92         date = adapter.formatDateTime(calendar);
93         assertEquals("2012-01-02T23:00:01.000Z", date);
94     }
95 
testSendDeletedItems()96     public void testSendDeletedItems() throws IOException {
97         setupAccountMailboxAndMessages(0);
98         // Setup our adapter and parser
99         setupSyncParserAndAdapter(mAccount, mMailbox);
100 
101         Serializer s = new Serializer();
102         ArrayList<Long> ids = new ArrayList<Long>();
103         ArrayList<Long> deletedIds = new ArrayList<Long>();
104 
105         // Create account and two mailboxes
106         mSyncAdapter.mAccount = mAccount;
107         Mailbox box1 = EmailContentSetupUtils.setupMailbox("box1", mAccount.mId, true,
108                 mProviderContext);
109         mSyncAdapter.mMailbox = box1;
110 
111         // Create 3 messages
112         Message msg1 = EmailContentSetupUtils.setupMessage("message1", mAccount.mId, box1.mId,
113                 true, true, mProviderContext);
114         ids.add(msg1.mId);
115         Message msg2 = EmailContentSetupUtils.setupMessage("message2", mAccount.mId, box1.mId,
116                 true, true, mProviderContext);
117         ids.add(msg2.mId);
118         Message msg3 = EmailContentSetupUtils.setupMessage("message3", mAccount.mId, box1.mId,
119                 true, true, mProviderContext);
120         ids.add(msg3.mId);
121         assertEquals(3, EmailContent.count(mProviderContext, Message.CONTENT_URI, WHERE_ACCOUNT_KEY,
122                 getAccountArgument(mAccount.mId)));
123 
124         // Delete them
125         for (long id: ids) {
126             mResolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, id),
127                     null, null);
128         }
129 
130         // Confirm that the messages are in the proper table
131         assertEquals(0, EmailContent.count(mProviderContext, Message.CONTENT_URI, WHERE_ACCOUNT_KEY,
132                 getAccountArgument(mAccount.mId)));
133         assertEquals(3, EmailContent.count(mProviderContext, Message.DELETED_CONTENT_URI,
134                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
135 
136         // Call code to send deletions; the id's of the ones actually deleted will be in the
137         // deletedIds list
138         mSyncAdapter.sendDeletedItems(s, deletedIds, true);
139         assertEquals(3, deletedIds.size());
140 
141         // Clear this out for the next test
142         deletedIds.clear();
143 
144         // Create a new message
145         Message msg4 = EmailContentSetupUtils.setupMessage("message4", mAccount.mId, box1.mId,
146                 true, true, mProviderContext);
147         assertEquals(1, EmailContent.count(mProviderContext, Message.CONTENT_URI, WHERE_ACCOUNT_KEY,
148                 getAccountArgument(mAccount.mId)));
149         // Find the body for this message
150         Body body = Body.restoreBodyWithMessageId(mProviderContext, msg4.mId);
151         // Set its source message to msg2's id
152         ContentValues values = new ContentValues();
153         values.put(Body.SOURCE_MESSAGE_KEY, msg2.mId);
154         body.update(mProviderContext, values);
155 
156         // Now send deletions again; this time only two should get deleted; msg2 should NOT be
157         // deleted as it's referenced by msg4
158         mSyncAdapter.sendDeletedItems(s, deletedIds, true);
159         assertEquals(2, deletedIds.size());
160         assertFalse(deletedIds.contains(msg2.mId));
161     }
162 
getAccountArgument(long id)163     private String[] getAccountArgument(long id) {
164         ACCOUNT_ARGUMENT[0] = Long.toString(id);
165         return ACCOUNT_ARGUMENT;
166     }
167 
setupSyncParserAndAdapter(Account account, Mailbox mailbox)168     void setupSyncParserAndAdapter(Account account, Mailbox mailbox) throws IOException {
169         EasSyncService service = getTestService(account, mailbox);
170         mSyncAdapter = new EmailSyncAdapter(service);
171         mSyncParser = mSyncAdapter.new EasEmailSyncParser(getTestInputStream(), mSyncAdapter);
172     }
173 
setupAccountMailboxAndMessages(int numMessages)174     ArrayList<Long> setupAccountMailboxAndMessages(int numMessages) {
175         ArrayList<Long> ids = new ArrayList<Long>();
176 
177         // Create account and two mailboxes
178         mAccount = EmailContentSetupUtils.setupAccount("account", true, mProviderContext);
179         mMailbox = EmailContentSetupUtils.setupMailbox("box1", mAccount.mId, true,
180                 mProviderContext);
181 
182         for (int i = 0; i < numMessages; i++) {
183             Message msg = EmailContentSetupUtils.setupMessage("message" + i, mAccount.mId,
184                     mMailbox.mId, true, true, mProviderContext);
185             ids.add(msg.mId);
186         }
187 
188         assertEquals(numMessages, EmailContent.count(mProviderContext, Message.CONTENT_URI,
189                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
190         return ids;
191     }
192 
testDeleteParser()193     public void testDeleteParser() throws IOException {
194         // Setup some messages
195         ArrayList<Long> messageIds = setupAccountMailboxAndMessages(3);
196         ContentValues cv = new ContentValues();
197         cv.put(SyncColumns.SERVER_ID, TEST_SERVER_ID);
198         long deleteMessageId = messageIds.get(1);
199         mResolver.update(ContentUris.withAppendedId(Message.CONTENT_URI, deleteMessageId), cv,
200                 null, null);
201 
202         // Setup our adapter and parser
203         setupSyncParserAndAdapter(mAccount, mMailbox);
204 
205         // Set up an input stream with a delete command
206         Serializer s = new Serializer(false);
207         s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, TEST_SERVER_ID).end().done();
208         byte[] bytes = s.toByteArray();
209         mSyncParser.resetInput(new ByteArrayInputStream(bytes));
210         mSyncParser.nextTag(0);
211 
212         // Run the delete parser
213         ArrayList<Long> deleteList = new ArrayList<Long>();
214         mSyncParser.deleteParser(deleteList, Tags.SYNC_DELETE);
215         // It should have found the message
216         assertEquals(1, deleteList.size());
217         long id = deleteList.get(0);
218         // And the id's should match
219         assertEquals(deleteMessageId, id);
220     }
221 
testChangeParser()222     public void testChangeParser() throws IOException {
223         // Setup some messages
224         ArrayList<Long> messageIds = setupAccountMailboxAndMessages(3);
225         ContentValues cv = new ContentValues();
226         int randomFlags = Message.FLAG_INCOMING_MEETING_CANCEL | Message.FLAG_TYPE_FORWARD;
227         cv.put(SyncColumns.SERVER_ID, TEST_SERVER_ID);
228         cv.put(MessageColumns.FLAGS, randomFlags);
229         long changeMessageId = messageIds.get(1);
230         mResolver.update(ContentUris.withAppendedId(Message.CONTENT_URI, changeMessageId), cv,
231                 null, null);
232 
233         // Setup our adapter and parser
234         setupSyncParserAndAdapter(mAccount, mMailbox);
235 
236         // Set up an input stream with a change command (marking TEST_SERVER_ID unread)
237         // Note that the test message creation code sets read to "true"
238         Serializer s = new Serializer(false);
239         s.start(Tags.SYNC_CHANGE).data(Tags.SYNC_SERVER_ID, TEST_SERVER_ID);
240         s.start(Tags.SYNC_APPLICATION_DATA);
241         s.data(Tags.EMAIL_READ, "0");
242         s.data(Tags.EMAIL2_LAST_VERB_EXECUTED,
243                 Integer.toString(EmailSyncAdapter.LAST_VERB_FORWARD));
244         s.end().end().done();
245         byte[] bytes = s.toByteArray();
246         mSyncParser.resetInput(new ByteArrayInputStream(bytes));
247         mSyncParser.nextTag(0);
248 
249         // Run the delete parser
250         ArrayList<ServerChange> changeList = new ArrayList<ServerChange>();
251         mSyncParser.changeParser(changeList);
252         // It should have found the message
253         assertEquals(1, changeList.size());
254         // And the id's should match
255         ServerChange change = changeList.get(0);
256         assertEquals(changeMessageId, change.id);
257         assertNotNull(change.read);
258         assertFalse(change.read);
259         // Make sure we see the forwarded flag AND that the original flags are preserved
260         assertEquals((Integer)(randomFlags | Message.FLAG_FORWARDED), change.flags);
261     }
262 
testCleanup()263     public void testCleanup() throws IOException {
264         // Setup some messages
265         ArrayList<Long> messageIds = setupAccountMailboxAndMessages(3);
266         // Setup our adapter and parser
267         setupSyncParserAndAdapter(mAccount, mMailbox);
268 
269         // Delete two of the messages, change one
270         long id = messageIds.get(0);
271         mResolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI, id),
272                 null, null);
273         mSyncAdapter.mDeletedIdList.add(id);
274         id = messageIds.get(1);
275         mResolver.delete(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI,
276                 id), null, null);
277         mSyncAdapter.mDeletedIdList.add(id);
278         id = messageIds.get(2);
279         ContentValues cv = new ContentValues();
280         cv.put(Message.FLAG_READ, 0);
281         mResolver.update(ContentUris.withAppendedId(Message.SYNCED_CONTENT_URI,
282                 id), cv, null, null);
283         mSyncAdapter.mUpdatedIdList.add(id);
284 
285         // The changed message should still exist
286         assertEquals(1, EmailContent.count(mProviderContext, Message.CONTENT_URI, WHERE_ACCOUNT_KEY,
287                 getAccountArgument(mAccount.mId)));
288 
289         // As well, the two deletions and one update
290         assertEquals(2, EmailContent.count(mProviderContext, Message.DELETED_CONTENT_URI,
291                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
292         assertEquals(1, EmailContent.count(mProviderContext, Message.UPDATED_CONTENT_URI,
293                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
294 
295         // Cleanup (i.e. after sync); should remove items from delete/update tables
296         mSyncAdapter.cleanup();
297 
298         // The three should be gone
299         assertEquals(0, EmailContent.count(mProviderContext, Message.DELETED_CONTENT_URI,
300                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
301         assertEquals(0, EmailContent.count(mProviderContext, Message.UPDATED_CONTENT_URI,
302                 WHERE_ACCOUNT_KEY, getAccountArgument(mAccount.mId)));
303     }
304 }
305