• 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.ContentResolver;
20 import android.content.res.AssetManager;
21 import android.database.Cursor;
22 import android.test.suitebuilder.annotation.MediumTest;
23 
24 import com.android.emailcommon.provider.Account;
25 import com.android.emailcommon.provider.EmailContent;
26 import com.android.emailcommon.provider.Mailbox;
27 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
28 import com.android.emailcommon.service.SyncWindow;
29 import com.android.exchange.CommandStatusException;
30 import com.android.exchange.EasSyncService;
31 import com.android.exchange.provider.EmailContentSetupUtils;
32 
33 import java.io.BufferedReader;
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.io.InputStreamReader;
37 import java.util.HashMap;
38 
39 /**
40  * You can run this entire test case with:
41  *   runtest -c com.android.exchange.adapter.FolderSyncParserTests exchange
42  */
43 @MediumTest
44 public class FolderSyncParserTests extends SyncAdapterTestCase<EmailSyncAdapter> {
45 
46     // We increment this to generate unique server id's
47     private int mServerIdCount = 0;
48     private final long mCreationTime = System.currentTimeMillis();
49     private final String[] mMailboxQueryArgs = new String[2];
50 
FolderSyncParserTests()51     public FolderSyncParserTests() {
52         super();
53     }
54 
setupBoxSync(int interval, int lookback, String serverId)55     private Mailbox setupBoxSync(int interval, int lookback, String serverId) {
56         // Don't save the box; just create it, and give it a server id
57         Mailbox box = EmailContentSetupUtils.setupMailbox("box1", mAccount.mId, false,
58                 mProviderContext, Mailbox.TYPE_MAIL);
59         box.mSyncInterval = interval;
60         box.mSyncLookback = lookback;
61         if (serverId != null) {
62             box.mServerId = serverId;
63         } else {
64             box.mServerId = "serverId-" + mCreationTime + '-' + mServerIdCount++;
65         }
66         box.save(mProviderContext);
67         return box;
68     }
69 
syncOptionsSame(Mailbox a, Mailbox b)70     private boolean syncOptionsSame(Mailbox a, Mailbox b) {
71         if (a.mSyncInterval != b.mSyncInterval) return false;
72         if (a.mSyncLookback != b.mSyncLookback) return false;
73         return true;
74     }
75 
brokentestSaveAndRestoreMailboxSyncOptions()76     public void brokentestSaveAndRestoreMailboxSyncOptions() throws IOException {
77         EasSyncService service = getTestService();
78         EmailSyncAdapter adapter = new EmailSyncAdapter(service);
79         FolderSyncParser parser = new FolderSyncParser(getTestInputStream(), adapter);
80         mAccount.save(mProviderContext);
81 
82         parser.mAccount = mAccount;
83         parser.mAccountId = mAccount.mId;
84         parser.mAccountIdAsString = Long.toString(mAccount.mId);
85         parser.mContext = mProviderContext;
86         parser.mContentResolver = mProviderContext.getContentResolver();
87 
88         // Don't save the box; just create it, and give it a server id
89         Mailbox box1 = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_ACCOUNT,
90                 null);
91         Mailbox box2 = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_ACCOUNT,
92                 null);
93         Mailbox boxa = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_1_MONTH,
94                 null);
95         Mailbox boxb = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_2_WEEKS,
96                 null);
97         Mailbox boxc = setupBoxSync(Account.CHECK_INTERVAL_PUSH, SyncWindow.SYNC_WINDOW_ACCOUNT,
98                 null);
99         Mailbox boxd = setupBoxSync(Account.CHECK_INTERVAL_PUSH, SyncWindow.SYNC_WINDOW_ACCOUNT,
100                 null);
101         Mailbox boxe = setupBoxSync(Account.CHECK_INTERVAL_PUSH, SyncWindow.SYNC_WINDOW_1_DAY,
102                 null);
103 
104         // Save the options (for a, b, c, d, e);
105         parser.saveMailboxSyncOptions();
106         // There should be 5 entries in the map, and they should be the correct ones
107         assertNotNull(parser.mSyncOptionsMap.get(boxa.mServerId));
108         assertNotNull(parser.mSyncOptionsMap.get(boxb.mServerId));
109         assertNotNull(parser.mSyncOptionsMap.get(boxc.mServerId));
110         assertNotNull(parser.mSyncOptionsMap.get(boxd.mServerId));
111         assertNotNull(parser.mSyncOptionsMap.get(boxe.mServerId));
112 
113         // Delete all the mailboxes in the account
114         ContentResolver cr = mProviderContext.getContentResolver();
115         cr.delete(Mailbox.CONTENT_URI, Mailbox.ACCOUNT_KEY + "=?",
116                 new String[] {parser.mAccountIdAsString});
117 
118         // Create new boxes, all with default values for interval & window
119         Mailbox box1x = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_ACCOUNT,
120                 box1.mServerId);
121         Mailbox box2x = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_ACCOUNT,
122                 box2.mServerId);
123         Mailbox boxax = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_ACCOUNT,
124                 boxa.mServerId);
125         Mailbox boxbx = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_ACCOUNT,
126                 boxb.mServerId);
127         Mailbox boxcx = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_ACCOUNT,
128                 boxc.mServerId);
129         Mailbox boxdx = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_ACCOUNT,
130                 boxd.mServerId);
131         Mailbox boxex = setupBoxSync(Account.CHECK_INTERVAL_NEVER, SyncWindow.SYNC_WINDOW_ACCOUNT,
132                 boxe.mServerId);
133 
134         // Restore the sync options
135         parser.restoreMailboxSyncOptions();
136         box1x = Mailbox.restoreMailboxWithId(mProviderContext, box1x.mId);
137         box2x = Mailbox.restoreMailboxWithId(mProviderContext, box2x.mId);
138         boxax = Mailbox.restoreMailboxWithId(mProviderContext, boxax.mId);
139         boxbx = Mailbox.restoreMailboxWithId(mProviderContext, boxbx.mId);
140         boxcx = Mailbox.restoreMailboxWithId(mProviderContext, boxcx.mId);
141         boxdx = Mailbox.restoreMailboxWithId(mProviderContext, boxdx.mId);
142         boxex = Mailbox.restoreMailboxWithId(mProviderContext, boxex.mId);
143 
144         assertTrue(syncOptionsSame(box1, box1x));
145         assertTrue(syncOptionsSame(box2, box2x));
146         assertTrue(syncOptionsSame(boxa, boxax));
147         assertTrue(syncOptionsSame(boxb, boxbx));
148         assertTrue(syncOptionsSame(boxc, boxcx));
149         assertTrue(syncOptionsSame(boxd, boxdx));
150         assertTrue(syncOptionsSame(boxe, boxex));
151     }
152 
153     private static class MockFolderSyncParser extends FolderSyncParser {
154         private BufferedReader mReader;
155         private int mDepth = 0;
156         private String[] mStack = new String[32];
157         private HashMap<String, Integer> mTagMap;
158 
159 
MockFolderSyncParser(String fileName, AbstractSyncAdapter adapter)160         public MockFolderSyncParser(String fileName, AbstractSyncAdapter adapter)
161                 throws IOException {
162             super(null, adapter);
163             AssetManager am = mContext.getAssets();
164             InputStream is = am.open(fileName);
165             if (is != null) {
166                 mReader = new BufferedReader(new InputStreamReader(is));
167             }
168         }
169 
initTagMap()170         private void initTagMap() {
171             mTagMap = new HashMap<String, Integer>();
172             int pageNum = 0;
173             for (String[] page: Tags.pages) {
174                 int tagNum = 5;
175                 for (String tag: page) {
176                     if (mTagMap.containsKey(tag)) {
177                         System.err.println("Duplicate tag: " + tag);
178                     }
179                     int val = (pageNum << Tags.PAGE_SHIFT) + tagNum;
180                     mTagMap.put(tag, val);
181                     tagNum++;
182                 }
183                 pageNum++;
184             }
185         }
186 
lookupTag(String tagName)187         private int lookupTag(String tagName) {
188             if (mTagMap == null) {
189                 initTagMap();
190             }
191             int res = mTagMap.get(tagName);
192             return res;
193         }
194 
getLine()195         private String getLine() throws IOException {
196             while (true) {
197                 String line = mReader.readLine();
198                 if (line == null) {
199                     return null;
200                 }
201                 int start = line.indexOf("| ");
202                 if (start > 2) {
203                     return line.substring(start + 2);
204                 }
205                 // Keep looking for a suitable line
206             }
207         }
208 
209         @Override
getValueInt()210         public int getValueInt() throws IOException {
211             return Integer.parseInt(getValue());
212         }
213 
214         @Override
getValue()215         public String getValue() throws IOException {
216             String line = getLine();
217             if (line == null) throw new IOException();
218             int start = line.indexOf(": ");
219             if (start < 0) throw new IOException("Line has no value: " + line);
220             try {
221                 return line.substring(start + 2).trim();
222             } finally {
223                 if (nextTag(0) != END) {
224                     throw new IOException("Value not followed by end tag: " + name);
225                 }
226             }
227         }
228 
229         @Override
skipTag()230         public void skipTag() throws IOException {
231             if (nextTag(0) == -1) {
232                 nextTag(0);
233             }
234         }
235 
236         @Override
nextTag(int endingTag)237         public int nextTag(int endingTag) throws IOException {
238             String line = getLine();
239             if (line == null) {
240                 return DONE;
241             }
242             if (line.startsWith("</")) {
243                 int end = line.indexOf('>');
244                 String tagName = line.substring(2, end).trim();
245                 if (!tagName.equals(mStack[--mDepth])) {
246                     throw new IOException("Tag end doesn't match tag");
247                 }
248                 mStack[mDepth] = null;
249                 return END;
250             } else if (line.startsWith("<")) {
251                 int end = line.indexOf('>');
252                 String tagName = line.substring(1, end).trim();
253                 mStack[mDepth++] = tagName;
254                 tag = lookupTag(tagName);
255                 return tag;
256             } else {
257                 return -1;
258             }
259         }
260     }
261 
getMailboxWithName(String folderName)262     private Mailbox getMailboxWithName(String folderName) {
263         mMailboxQueryArgs[1] = folderName;
264         Cursor c = mResolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
265                 Mailbox.ACCOUNT_KEY + "=? AND " + Mailbox.DISPLAY_NAME + "=?", mMailboxQueryArgs,
266                 null);
267         try {
268             assertTrue(c.getCount() == 1);
269             c.moveToFirst();
270             Mailbox m = new Mailbox();
271             m.restore(c);
272             return m;
273         } finally {
274             c.close();
275         }
276     }
277 
isTopLevel(String folderName)278     private boolean isTopLevel(String folderName) {
279         Mailbox m = getMailboxWithName(folderName);
280         assertNotNull(m);
281         return m.mParentKey == Mailbox.NO_MAILBOX;
282     }
283 
isSubfolder(String parentName, String childName)284     private boolean isSubfolder(String parentName, String childName) {
285         Mailbox parent = getMailboxWithName(parentName);
286         Mailbox child = getMailboxWithName(childName);
287         assertNotNull(parent);
288         assertNotNull(child);
289         assertTrue((parent.mFlags & Mailbox.FLAG_HAS_CHILDREN) != 0);
290         return child.mParentKey == parent.mId;
291     }
292 
293     /**
294      * Parse a set of EAS FolderSync commands and create the Mailbox tree accordingly
295      *
296      * @param fileName the name of the file containing emaillog data for folder sync
297      * @throws IOException
298      * @throws CommandStatusException
299      */
testComplexFolderListParse(String fileName)300     private void testComplexFolderListParse(String fileName) throws IOException,
301             CommandStatusException {
302         EasSyncService service = getTestService();
303         EmailSyncAdapter adapter = new EmailSyncAdapter(service);
304         FolderSyncParser parser = new MockFolderSyncParser(fileName, adapter);
305         mAccount.save(mProviderContext);
306         mMailboxQueryArgs[0] = Long.toString(mAccount.mId);
307         parser.mAccount = mAccount;
308         parser.mAccountId = mAccount.mId;
309         parser.mAccountIdAsString = Long.toString(mAccount.mId);
310         parser.mContext = mProviderContext;
311         parser.mContentResolver = mResolver;
312 
313         parser.parse();
314 
315         assertTrue(isTopLevel("Inbox"));
316         assertTrue(isSubfolder("Inbox", "Gecko"));
317         assertTrue(isSubfolder("Inbox", "Wombat"));
318         assertTrue(isSubfolder("Inbox", "Laslo"));
319         assertTrue(isSubfolder("Inbox", "Tomorrow"));
320         assertTrue(isSubfolder("Inbox", "Vader"));
321         assertTrue(isSubfolder("Inbox", "Personal"));
322         assertTrue(isSubfolder("Laslo", "Lego"));
323         assertTrue(isSubfolder("Tomorrow", "HomeRun"));
324         assertTrue(isSubfolder("Tomorrow", "Services"));
325         assertTrue(isSubfolder("HomeRun", "Review"));
326         assertTrue(isSubfolder("Vader", "Max"));
327         assertTrue(isSubfolder("Vader", "Parser"));
328         assertTrue(isSubfolder("Vader", "Scott"));
329         assertTrue(isSubfolder("Vader", "Surfing"));
330         assertTrue(isSubfolder("Max", "Thomas"));
331         assertTrue(isSubfolder("Personal", "Famine"));
332         assertTrue(isSubfolder("Personal", "Bar"));
333         assertTrue(isSubfolder("Personal", "Bill"));
334         assertTrue(isSubfolder("Personal", "Boss"));
335         assertTrue(isSubfolder("Personal", "Houston"));
336         assertTrue(isSubfolder("Personal", "Mistake"));
337         assertTrue(isSubfolder("Personal", "Online"));
338         assertTrue(isSubfolder("Personal", "Sports"));
339         assertTrue(isSubfolder("Famine", "Buffalo"));
340         assertTrue(isSubfolder("Famine", "CornedBeef"));
341         assertTrue(isSubfolder("Houston", "Rebar"));
342         assertTrue(isSubfolder("Mistake", "Intro"));
343     }
344 
345     // FolderSyncParserTest.txt is based on customer data (all names changed) that failed to
346     // properly create the Mailbox list
brokentestComplexFolderListParse1()347     public void brokentestComplexFolderListParse1() throws CommandStatusException, IOException {
348         testComplexFolderListParse("FolderSyncParserTest.txt");
349     }
350 
351     // As above, with the order changed (putting children before parents; a more difficult case
brokentestComplexFolderListParse2()352     public void brokentestComplexFolderListParse2() throws CommandStatusException, IOException {
353         testComplexFolderListParse("FolderSyncParserTest2.txt");
354     }
355 
356     // Much larger test (from user with issues related to Type 1 folders)
brokentestComplexFolderListParse3()357     public void brokentestComplexFolderListParse3() throws CommandStatusException, IOException {
358         EasSyncService service = getTestService();
359         EmailSyncAdapter adapter = new EmailSyncAdapter(service);
360         FolderSyncParser parser = new MockFolderSyncParser("FolderSyncParserTest3.txt", adapter);
361         mAccount.save(mProviderContext);
362         mMailboxQueryArgs[0] = Long.toString(mAccount.mId);
363         parser.mAccount = mAccount;
364         parser.mAccountId = mAccount.mId;
365         parser.mAccountIdAsString = Long.toString(mAccount.mId);
366         parser.mContext = mProviderContext;
367         parser.mContentResolver = mResolver;
368         parser.parse();
369 
370         int cnt = EmailContent.count(mProviderContext, Mailbox.CONTENT_URI,
371                 MailboxColumns.ACCOUNT_KEY + "=" + mAccount.mId, null);
372         // 270 in the file less 4 "conflicts" folders
373         assertEquals(266, cnt);
374     }
375 }
376