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