Home | History | Annotate | Download | only in adapter
      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 
     51     public FolderSyncParserTests() {
     52         super();
     53     }
     54 
     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 
     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 
     76     public void testSaveAndRestoreMailboxSyncOptions() 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 
    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 
    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 
    187         private int lookupTag(String tagName) {
    188             if (mTagMap == null) {
    189                 initTagMap();
    190             }
    191             int res = mTagMap.get(tagName);
    192             return res;
    193         }
    194 
    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
    210         public int getValueInt() throws IOException {
    211             return Integer.parseInt(getValue());
    212         }
    213 
    214         @Override
    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
    230         public void skipTag() throws IOException {
    231             if (nextTag(0) == -1) {
    232                 nextTag(0);
    233             }
    234         }
    235 
    236         @Override
    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 
    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 
    278     private boolean isTopLevel(String folderName) {
    279         Mailbox m = getMailboxWithName(folderName);
    280         assertNotNull(m);
    281         return m.mParentKey == Mailbox.NO_MAILBOX;
    282     }
    283 
    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      */
    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
    347     public void testComplexFolderListParse1() throws CommandStatusException, IOException {
    348         testComplexFolderListParse("FolderSyncParserTest.txt");
    349     }
    350 
    351     // As above, with the order changed (putting children before parents; a more difficult case
    352     public void testComplexFolderListParse2() throws CommandStatusException, IOException {
    353         testComplexFolderListParse("FolderSyncParserTest2.txt");
    354     }
    355 
    356     // Much larger test (from user with issues related to Type 1 folders)
    357     public void testComplexFolderListParse3() 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